Skip to content

Commit 8c8e0eb

Browse files
Merge pull request #354 from swiftwasm/maxd/embedded-concurrency2
Fix `JavaScriptEventLoop` not building with Embedded Swift
2 parents dd20832 + 697f06b commit 8c8e0eb

File tree

5 files changed

+34
-29
lines changed

5 files changed

+34
-29
lines changed

Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift

+4-7
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,20 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
105105
return eventLoop
106106
}
107107

108-
@MainActor private static var didInstallGlobalExecutor = false
108+
private nonisolated(unsafe) static var didInstallGlobalExecutor = false
109109

110110
/// Set JavaScript event loop based executor to be the global executor
111111
/// Note that this should be called before any of the jobs are created.
112112
/// This installation step will be unnecessary after custom executor are
113113
/// introduced officially. See also [a draft proposal for custom
114114
/// executors](https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0000-custom-executors.md#the-default-global-concurrent-executor)
115115
public static func installGlobalExecutor() {
116-
MainActor.assumeIsolated {
117-
Self.installGlobalExecutorIsolated()
118-
}
116+
Self.installGlobalExecutorIsolated()
119117
}
120118

121-
@MainActor private static func installGlobalExecutorIsolated() {
119+
private static func installGlobalExecutorIsolated() {
122120
guard !didInstallGlobalExecutor else { return }
121+
didInstallGlobalExecutor = true
123122

124123
#if compiler(>=5.9)
125124
typealias swift_task_asyncMainDrainQueue_hook_Fn = @convention(thin) (
@@ -188,8 +187,6 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
188187
swift_task_enqueueMainExecutor_hook_impl,
189188
to: UnsafeMutableRawPointer?.self
190189
)
191-
192-
didInstallGlobalExecutor = true
193190
}
194191

195192
private func enqueue(_ job: UnownedJob, withDelay nanoseconds: UInt64) {

Sources/JavaScriptKit/BasicObjects/JSPromise.swift

+19-14
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,22 @@ public final class JSPromise: JSBridgedClass {
8484
}
8585
#endif
8686

87-
#if !hasFeature(Embedded)
8887
/// Schedules the `success` closure to be invoked on successful completion of `self`.
8988
@discardableResult
90-
public func then(success: @escaping (JSValue) -> ConvertibleToJSValue) -> JSPromise {
89+
public func then(success: @escaping (JSValue) -> JSValue) -> JSPromise {
9190
let closure = JSOneshotClosure {
9291
success($0[0]).jsValue
9392
}
9493
return JSPromise(unsafelyWrapping: jsObject.then!(closure).object!)
9594
}
9695

97-
#if compiler(>=5.5)
96+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
9897
/// Schedules the `success` closure to be invoked on successful completion of `self`.
9998
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
10099
@discardableResult
101-
public func then(success: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue) -> JSPromise {
100+
public func then(
101+
success: sending @escaping (sending JSValue) async throws -> JSValue
102+
) -> JSPromise {
102103
let closure = JSOneshotClosure.async {
103104
try await success($0[0]).jsValue
104105
}
@@ -109,8 +110,8 @@ public final class JSPromise: JSBridgedClass {
109110
/// Schedules the `success` closure to be invoked on successful completion of `self`.
110111
@discardableResult
111112
public func then(
112-
success: @escaping (sending JSValue) -> ConvertibleToJSValue,
113-
failure: @escaping (sending JSValue) -> ConvertibleToJSValue
113+
success: @escaping (sending JSValue) -> JSValue,
114+
failure: @escaping (sending JSValue) -> JSValue
114115
) -> JSPromise {
115116
let successClosure = JSOneshotClosure {
116117
success($0[0]).jsValue
@@ -121,13 +122,13 @@ public final class JSPromise: JSBridgedClass {
121122
return JSPromise(unsafelyWrapping: jsObject.then!(successClosure, failureClosure).object!)
122123
}
123124

124-
#if compiler(>=5.5)
125+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
125126
/// Schedules the `success` closure to be invoked on successful completion of `self`.
126127
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
127128
@discardableResult
128129
public func then(
129-
success: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue,
130-
failure: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue
130+
success: sending @escaping (sending JSValue) async throws -> JSValue,
131+
failure: sending @escaping (sending JSValue) async throws -> JSValue
131132
) -> JSPromise {
132133
let successClosure = JSOneshotClosure.async {
133134
try await success($0[0]).jsValue
@@ -141,19 +142,24 @@ public final class JSPromise: JSBridgedClass {
141142

142143
/// Schedules the `failure` closure to be invoked on rejected completion of `self`.
143144
@discardableResult
144-
public func `catch`(failure: @escaping (sending JSValue) -> ConvertibleToJSValue) -> JSPromise {
145+
public func `catch`(
146+
failure: @escaping (sending JSValue) -> JSValue
147+
)
148+
-> JSPromise
149+
{
145150
let closure = JSOneshotClosure {
146151
failure($0[0]).jsValue
147152
}
148153
return .init(unsafelyWrapping: jsObject.catch!(closure).object!)
149154
}
150155

151-
#if compiler(>=5.5)
156+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
152157
/// Schedules the `failure` closure to be invoked on rejected completion of `self`.
153158
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
154159
@discardableResult
155-
public func `catch`(failure: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue) -> JSPromise
156-
{
160+
public func `catch`(
161+
failure: sending @escaping (sending JSValue) async throws -> JSValue
162+
) -> JSPromise {
157163
let closure = JSOneshotClosure.async {
158164
try await failure($0[0]).jsValue
159165
}
@@ -171,5 +177,4 @@ public final class JSPromise: JSBridgedClass {
171177
}
172178
return .init(unsafelyWrapping: jsObject.finally!(closure).object!)
173179
}
174-
#endif
175180
}

Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import _CJavaScriptKit
2+
#if hasFeature(Embedded) && os(WASI)
3+
import _Concurrency
4+
#endif
25

36
/// `JSClosureProtocol` wraps Swift closure objects for use in JavaScript. Conforming types
47
/// are responsible for managing the lifetime of the closure they wrap, but can delegate that
@@ -40,7 +43,7 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
4043
fatalError("JSOneshotClosure does not support dictionary literal initialization")
4144
}
4245

43-
#if compiler(>=5.5) && !hasFeature(Embedded)
46+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
4447
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
4548
public static func async(_ body: sending @escaping (sending [JSValue]) async throws -> JSValue) -> JSOneshotClosure
4649
{
@@ -132,7 +135,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
132135
fatalError("JSClosure does not support dictionary literal initialization")
133136
}
134137

135-
#if compiler(>=5.5) && !hasFeature(Embedded)
138+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
136139
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
137140
public static func async(_ body: @Sendable @escaping (sending [JSValue]) async throws -> JSValue) -> JSClosure {
138141
JSClosure(makeAsyncClosure(body))
@@ -148,7 +151,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
148151
#endif
149152
}
150153

151-
#if compiler(>=5.5) && !hasFeature(Embedded)
154+
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
152155
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
153156
private func makeAsyncClosure(
154157
_ body: sending @escaping (sending [JSValue]) async throws -> JSValue

Tests/JavaScriptEventLoopTests/JSPromiseTests.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ final class JSPromiseTests: XCTestCase {
99
p1 = p1.then { value in
1010
XCTAssertEqual(value, .null)
1111
continuation.resume()
12-
return JSValue.number(1.0)
12+
return JSValue.number(1.0).jsValue
1313
}
1414
}
1515
await withCheckedContinuation { continuation in
1616
p1 = p1.then { value in
1717
XCTAssertEqual(value, .number(1.0))
1818
continuation.resume()
19-
return JSPromise.resolve(JSValue.boolean(true))
19+
return JSPromise.resolve(JSValue.boolean(true)).jsValue
2020
}
2121
}
2222
await withCheckedContinuation { continuation in
@@ -48,7 +48,7 @@ final class JSPromiseTests: XCTestCase {
4848
p2 = p2.then { value in
4949
XCTAssertEqual(value, .boolean(true))
5050
continuation.resume()
51-
return JSPromise.reject(JSValue.number(2.0))
51+
return JSPromise.reject(JSValue.number(2.0)).jsValue
5252
}
5353
}
5454
await withCheckedContinuation { continuation in

Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ final class JavaScriptEventLoopTests: XCTestCase {
151151
}
152152
let promise2 = promise.then { result in
153153
try await Task.sleep(nanoseconds: 100_000_000)
154-
return String(result.number!)
154+
return .string(String(result.number!))
155155
}
156156
let thenDiff = try await measureTime {
157157
let result = try await promise2.value
@@ -171,7 +171,7 @@ final class JavaScriptEventLoopTests: XCTestCase {
171171
100
172172
)
173173
}
174-
let failingPromise2 = failingPromise.then { _ in
174+
let failingPromise2 = failingPromise.then { _ -> JSValue in
175175
throw MessageError("Should not be called", file: #file, line: #line, column: #column)
176176
} failure: { err in
177177
return err

0 commit comments

Comments
 (0)