Skip to content

Commit dc1f09b

Browse files
committed
Fix JavaScriptEventLoop not building with Embedded Swift
The change fixes some issues in the JavaScriptKit library when build with Embedded Swift support. Specifically, `@MainActor` type is not available in Embedded Swift, thus `Atomic` type is used instead. Similarly, existential types are not available either, so they're replaced with concrete `some` types and generics.
1 parent dd20832 commit dc1f09b

File tree

3 files changed

+40
-13
lines changed

3 files changed

+40
-13
lines changed

Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import _Concurrency
33
import _CJavaScriptEventLoop
44
import _CJavaScriptKit
55

6+
#if hasFeature(Embedded)
7+
import Synchronization
8+
#endif
9+
610
// NOTE: `@available` annotations are semantically wrong, but they make it easier to develop applications targeting WebAssembly in Xcode.
711

812
#if compiler(>=5.5)
@@ -105,21 +109,39 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
105109
return eventLoop
106110
}
107111

108-
@MainActor private static var didInstallGlobalExecutor = false
112+
#if !hasFeature(Embedded)
113+
@MainActor
114+
private static var didInstallGlobalExecutor = false
115+
#else
116+
private static let didInstallGlobalExecutor = Atomic<Bool>(false)
117+
#endif
109118

110119
/// Set JavaScript event loop based executor to be the global executor
111120
/// Note that this should be called before any of the jobs are created.
112121
/// This installation step will be unnecessary after custom executor are
113122
/// introduced officially. See also [a draft proposal for custom
114123
/// executors](https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0000-custom-executors.md#the-default-global-concurrent-executor)
115124
public static func installGlobalExecutor() {
125+
#if !hasFeature(Embedded)
116126
MainActor.assumeIsolated {
117127
Self.installGlobalExecutorIsolated()
118128
}
129+
#else
130+
Self.installGlobalExecutorIsolated()
131+
#endif
119132
}
120133

121-
@MainActor private static func installGlobalExecutorIsolated() {
134+
#if !hasFeature(Embedded)
135+
@MainActor
136+
#endif
137+
private static func installGlobalExecutorIsolated() {
138+
#if !hasFeature(Embedded)
122139
guard !didInstallGlobalExecutor else { return }
140+
#else
141+
guard !didInstallGlobalExecutor.load(ordering: .sequentiallyConsistent) else {
142+
return
143+
}
144+
#endif
123145

124146
#if compiler(>=5.9)
125147
typealias swift_task_asyncMainDrainQueue_hook_Fn = @convention(thin) (
@@ -189,7 +211,11 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
189211
to: UnsafeMutableRawPointer?.self
190212
)
191213

214+
#if !hasFeature(Embedded)
192215
didInstallGlobalExecutor = true
216+
#else
217+
didInstallGlobalExecutor.store(true, ordering: .sequentiallyConsistent)
218+
#endif
193219
}
194220

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

Sources/JavaScriptKit/BasicObjects/JSPromise.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,9 @@ 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) -> some ConvertibleToJSValue) -> JSPromise {
9190
let closure = JSOneshotClosure {
9291
success($0[0]).jsValue
9392
}
@@ -98,7 +97,7 @@ public final class JSPromise: JSBridgedClass {
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(success: sending @escaping (sending JSValue) async throws -> some ConvertibleToJSValue) -> JSPromise {
102101
let closure = JSOneshotClosure.async {
103102
try await success($0[0]).jsValue
104103
}
@@ -109,8 +108,8 @@ public final class JSPromise: JSBridgedClass {
109108
/// Schedules the `success` closure to be invoked on successful completion of `self`.
110109
@discardableResult
111110
public func then(
112-
success: @escaping (sending JSValue) -> ConvertibleToJSValue,
113-
failure: @escaping (sending JSValue) -> ConvertibleToJSValue
111+
success: @escaping (sending JSValue) -> some ConvertibleToJSValue,
112+
failure: @escaping (sending JSValue) -> some ConvertibleToJSValue
114113
) -> JSPromise {
115114
let successClosure = JSOneshotClosure {
116115
success($0[0]).jsValue
@@ -126,8 +125,8 @@ public final class JSPromise: JSBridgedClass {
126125
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
127126
@discardableResult
128127
public func then(
129-
success: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue,
130-
failure: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue
128+
success: sending @escaping (sending JSValue) async throws -> some ConvertibleToJSValue,
129+
failure: sending @escaping (sending JSValue) async throws -> some ConvertibleToJSValue
131130
) -> JSPromise {
132131
let successClosure = JSOneshotClosure.async {
133132
try await success($0[0]).jsValue
@@ -141,7 +140,9 @@ public final class JSPromise: JSBridgedClass {
141140

142141
/// Schedules the `failure` closure to be invoked on rejected completion of `self`.
143142
@discardableResult
144-
public func `catch`(failure: @escaping (sending JSValue) -> ConvertibleToJSValue) -> JSPromise {
143+
public func `catch`(failure: @escaping (sending JSValue) -> some ConvertibleToJSValue)
144+
-> JSPromise
145+
{
145146
let closure = JSOneshotClosure {
146147
failure($0[0]).jsValue
147148
}
@@ -152,7 +153,7 @@ public final class JSPromise: JSBridgedClass {
152153
/// Schedules the `failure` closure to be invoked on rejected completion of `self`.
153154
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
154155
@discardableResult
155-
public func `catch`(failure: sending @escaping (sending JSValue) async throws -> ConvertibleToJSValue) -> JSPromise
156+
public func `catch`(failure: sending @escaping (sending JSValue) async throws -> some ConvertibleToJSValue) -> JSPromise
156157
{
157158
let closure = JSOneshotClosure.async {
158159
try await failure($0[0]).jsValue
@@ -171,5 +172,4 @@ public final class JSPromise: JSBridgedClass {
171172
}
172173
return .init(unsafelyWrapping: jsObject.finally!(closure).object!)
173174
}
174-
#endif
175175
}

Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _CJavaScriptKit
2+
import _Concurrency
23

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

43-
#if compiler(>=5.5) && !hasFeature(Embedded)
44+
#if compiler(>=5.5)
4445
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
4546
public static func async(_ body: sending @escaping (sending [JSValue]) async throws -> JSValue) -> JSOneshotClosure
4647
{

0 commit comments

Comments
 (0)