From 82d37e9d07a1fa1e4eabf29c5381c41087982410 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 22:40:39 +0100 Subject: [PATCH 01/12] drop swift 6.1 --- .github/workflows/test.yml | 6 +- Examples/Embedded/README.md | 2 +- Package@swift-6.1.swift | 222 ------------------ Plugins/PackageToJS/Tests/ExampleTests.swift | 7 - Sources/JavaScriptEventLoop/JSRemote.swift | 8 +- Sources/JavaScriptEventLoop/JSSending.swift | 17 +- .../JavaScriptEventLoop.swift | 6 +- .../WebWorkerTaskExecutor.swift | 6 +- .../FundamentalObjects/JSObject.swift | 10 +- Sources/JavaScriptKit/ThreadLocal.swift | 2 +- .../WebWorkerDedicatedExecutorTests.swift | 2 +- .../WebWorkerTaskExecutorTests.swift | 2 +- 12 files changed, 23 insertions(+), 267 deletions(-) delete mode 100644 Package@swift-6.1.swift diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index baba9c79b..7621fad53 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: entry: - os: ubuntu-22.04 toolchain: - download-url: https://download.swift.org/swift-6.1-release/ubuntu2204/swift-6.1-RELEASE/swift-6.1-RELEASE-ubuntu22.04.tar.gz + download-url: https://download.swift.org/swift-6.2-release/ubuntu2204/swift-6.2-RELEASE/swift-6.2-RELEASE-ubuntu22.04.tar.gz wasi-backend: Node target: "wasm32-unknown-wasi" env: | @@ -77,8 +77,6 @@ jobs: strategy: matrix: entry: - - image: "swift:6.1.2" - swift-syntax-version: "601.0.0" - image: "swift:6.2" swift-syntax-version: "602.0.0" - image: "swift:6.3" @@ -134,7 +132,7 @@ jobs: format: runs-on: ubuntu-latest container: - image: swift:6.1.2 + image: swift:6.2 steps: - uses: actions/checkout@v6 - run: ./Utilities/format.swift diff --git a/Examples/Embedded/README.md b/Examples/Embedded/README.md index e99d659ff..97e2490b7 100644 --- a/Examples/Embedded/README.md +++ b/Examples/Embedded/README.md @@ -1,6 +1,6 @@ # Embedded example -Requires a recent DEVELOPMENT-SNAPSHOT toolchain. (tested with swift-6.1-DEVELOPMENT-SNAPSHOT-2025-02-21-a) +Requires a recent DEVELOPMENT-SNAPSHOT toolchain. ```sh $ ./build.sh diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift deleted file mode 100644 index fe98ec529..000000000 --- a/Package@swift-6.1.swift +++ /dev/null @@ -1,222 +0,0 @@ -// swift-tools-version:6.1 - -import CompilerPluginSupport -import PackageDescription - -// NOTE: needed for embedded customizations, ideally this will not be necessary at all in the future, or can be replaced with traits -let shouldBuildForEmbedded = Context.environment["JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM"].flatMap(Bool.init) ?? false -let useLegacyResourceBundling = - Context.environment["JAVASCRIPTKIT_USE_LEGACY_RESOURCE_BUNDLING"].flatMap(Bool.init) ?? false - -let testingLinkerFlags: [LinkerSetting] = [ - .unsafeFlags( - [ - "-Xlinker", "--stack-first", - "-Xlinker", "--global-base=524288", - "-Xlinker", "-z", - "-Xlinker", "stack-size=524288", - ], - .when(platforms: [.wasi]) - ) -] - -let package = Package( - name: "JavaScriptKit", - platforms: [ - .macOS(.v13), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6), - .macCatalyst(.v13), - ], - products: [ - .library(name: "JavaScriptKit", targets: ["JavaScriptKit"]), - .library(name: "JavaScriptEventLoop", targets: ["JavaScriptEventLoop"]), - .library(name: "JavaScriptBigIntSupport", targets: ["JavaScriptBigIntSupport"]), - .library(name: "JavaScriptFoundationCompat", targets: ["JavaScriptFoundationCompat"]), - .library(name: "JavaScriptEventLoopTestSupport", targets: ["JavaScriptEventLoopTestSupport"]), - .plugin(name: "PackageToJS", targets: ["PackageToJS"]), - .plugin(name: "BridgeJS", targets: ["BridgeJS"]), - .plugin(name: "BridgeJSCommandPlugin", targets: ["BridgeJSCommandPlugin"]), - ], - dependencies: [ - .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"603.0.0") - ], - targets: [ - .target( - name: "JavaScriptKit", - dependencies: ["_CJavaScriptKit", "BridgeJSMacros"], - exclude: useLegacyResourceBundling ? [] : ["Runtime"], - resources: useLegacyResourceBundling ? [.copy("Runtime")] : [], - cSettings: shouldBuildForEmbedded - ? [ - .unsafeFlags(["-fdeclspec"]) - ] : nil, - swiftSettings: [ - .enableExperimentalFeature("Extern") - ] - + (shouldBuildForEmbedded - ? [ - .enableExperimentalFeature("Embedded"), - .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"]), - ] : []) - ), - .target(name: "_CJavaScriptKit"), - .macro( - name: "BridgeJSMacros", - dependencies: [ - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), - ] - ), - - .testTarget( - name: "JavaScriptKitTests", - dependencies: ["JavaScriptKit"], - swiftSettings: [ - .enableExperimentalFeature("Extern") - ], - linkerSettings: testingLinkerFlags - ), - - .target( - name: "JavaScriptBigIntSupport", - dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"], - swiftSettings: shouldBuildForEmbedded - ? [ - .enableExperimentalFeature("Embedded"), - .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"]), - ] : [] - ), - .target(name: "_CJavaScriptBigIntSupport", dependencies: ["_CJavaScriptKit"]), - .testTarget( - name: "JavaScriptBigIntSupportTests", - dependencies: ["JavaScriptBigIntSupport", "JavaScriptKit"], - linkerSettings: testingLinkerFlags - ), - - .target( - name: "JavaScriptEventLoop", - dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"], - swiftSettings: shouldBuildForEmbedded - ? [ - .enableExperimentalFeature("Embedded"), - .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"]), - ] : [] - ), - .target(name: "_CJavaScriptEventLoop"), - .testTarget( - name: "JavaScriptEventLoopTests", - dependencies: [ - "JavaScriptEventLoop", - "JavaScriptKit", - "JavaScriptEventLoopTestSupport", - ], - swiftSettings: [ - .enableExperimentalFeature("Extern") - ], - linkerSettings: testingLinkerFlags - ), - .target( - name: "JavaScriptEventLoopTestSupport", - dependencies: [ - "_CJavaScriptEventLoopTestSupport", - "JavaScriptEventLoop", - ] - ), - .target(name: "_CJavaScriptEventLoopTestSupport"), - .testTarget( - name: "JavaScriptEventLoopTestSupportTests", - dependencies: [ - "JavaScriptKit", - "JavaScriptEventLoopTestSupport", - ], - linkerSettings: testingLinkerFlags - ), - .target( - name: "JavaScriptFoundationCompat", - dependencies: [ - "JavaScriptKit" - ] - ), - .testTarget( - name: "JavaScriptFoundationCompatTests", - dependencies: [ - "JavaScriptFoundationCompat" - ], - linkerSettings: testingLinkerFlags - ), - .plugin( - name: "PackageToJS", - capability: .command( - intent: .custom(verb: "js", description: "Convert a Swift package to a JavaScript package") - ), - path: "Plugins/PackageToJS/Sources" - ), - .plugin( - name: "BridgeJS", - capability: .buildTool(), - dependencies: ["BridgeJSTool"], - path: "Plugins/BridgeJS/Sources/BridgeJSBuildPlugin" - ), - .plugin( - name: "BridgeJSCommandPlugin", - capability: .command( - intent: .custom(verb: "bridge-js", description: "Generate bridging code"), - permissions: [.writeToPackageDirectory(reason: "Generate bridging code")] - ), - dependencies: ["BridgeJSTool"], - path: "Plugins/BridgeJS/Sources/BridgeJSCommandPlugin" - ), - .executableTarget( - name: "BridgeJSTool", - dependencies: [ - .product(name: "SwiftParser", package: "swift-syntax"), - .product(name: "SwiftSyntax", package: "swift-syntax"), - .product(name: "SwiftBasicFormat", package: "swift-syntax"), - .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), - ], - exclude: ["TS2Swift/JavaScript", "README.md"] - ), - .testTarget( - name: "BridgeJSRuntimeTests", - dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], - exclude: [ - "bridge-js.config.json", - "bridge-js.d.ts", - "bridge-js.global.d.ts", - "Generated/JavaScript", - "JavaScript", - ], - swiftSettings: [ - .enableExperimentalFeature("Extern") - ], - linkerSettings: testingLinkerFlags - ), - .testTarget( - name: "BridgeJSGlobalTests", - dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], - exclude: [ - "bridge-js.config.json", - "bridge-js.d.ts", - "Generated/JavaScript", - ], - swiftSettings: [ - .enableExperimentalFeature("Extern") - ], - linkerSettings: testingLinkerFlags - ), - .testTarget( - name: "BridgeJSIdentityTests", - dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], - exclude: [ - "bridge-js.config.json", - "Generated/JavaScript", - ], - swiftSettings: [ - .enableExperimentalFeature("Extern") - ], - linkerSettings: testingLinkerFlags - ), - ] -) diff --git a/Plugins/PackageToJS/Tests/ExampleTests.swift b/Plugins/PackageToJS/Tests/ExampleTests.swift index d26832771..1f5bedcdc 100644 --- a/Plugins/PackageToJS/Tests/ExampleTests.swift +++ b/Plugins/PackageToJS/Tests/ExampleTests.swift @@ -297,7 +297,6 @@ extension Trait where Self == ConditionTrait { } } - #if compiler(>=6.1) @Test(.requireSwiftSDK) func testingWithCoverage() throws { let swiftSDKID = try #require(Self.getSwiftSDKID()) @@ -325,7 +324,6 @@ extension Trait where Self == ConditionTrait { } } } - #endif #endif // compiler(>=6.3) @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) @@ -379,7 +377,6 @@ extension Trait where Self == ConditionTrait { } } - #if compiler(>=6.1) // TODO: Remove triple restriction once swift-testing is shipped in p1-threads SDK @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasi")) func continuationLeakInTest_SwiftTesting() throws { @@ -391,8 +388,6 @@ extension Trait where Self == ConditionTrait { try runSwift(["package", "--disable-sandbox", "--swift-sdk", swiftSDKID, "js", "test"], [:]) } } - #endif - @Test(.requireSwiftSDK) func playwrightOnPageLoad_XCTest() throws { let swiftSDKID = try #require(Self.getSwiftSDKID()) @@ -413,7 +408,6 @@ extension Trait where Self == ConditionTrait { } } - #if compiler(>=6.1) // TODO: Remove triple restriction once swift-testing is shipped in p1-threads SDK @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasi")) func playwrightOnPageLoad_SwiftTesting() throws { @@ -434,5 +428,4 @@ extension Trait where Self == ConditionTrait { ) } } - #endif } diff --git a/Sources/JavaScriptEventLoop/JSRemote.swift b/Sources/JavaScriptEventLoop/JSRemote.swift index 4f488d7b8..b85b4991c 100644 --- a/Sources/JavaScriptEventLoop/JSRemote.swift +++ b/Sources/JavaScriptEventLoop/JSRemote.swift @@ -62,7 +62,7 @@ extension JSRemote where T == JSObject { /// /// - Parameter object: The JavaScript object to reference remotely. public init(_ object: JSObject) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) self.init(sourceObject: object, sourceTid: object.ownerTid) #else self.init(sourceObject: object, sourceTid: -1) @@ -92,7 +92,7 @@ extension JSRemote where T == JSObject { public func withJSObject( _ body: @Sendable @escaping (JSObject) throws(E) -> R ) async throws(E) -> sending R { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) if storage.sourceTid == swjs_get_worker_thread_id_cached() { return try body(storage.sourceObject) } @@ -137,13 +137,11 @@ private final class _JSRemoteContext: @unchecked Sendable { } } -#if compiler(>=6.1) @_expose(wasm, "swjs_invoke_remote_jsobject_body") @_cdecl("swjs_invoke_remote_jsobject_body") -#endif @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func _swjs_invoke_remote_jsobject_body(_ contextPtr: UnsafeRawPointer?) -> Bool { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) guard let contextPtr else { return true } let context = Unmanaged<_JSRemoteContext>.fromOpaque(contextPtr).takeRetainedValue() diff --git a/Sources/JavaScriptEventLoop/JSSending.swift b/Sources/JavaScriptEventLoop/JSSending.swift index fb2fb1ddf..613abdd8a 100644 --- a/Sources/JavaScriptEventLoop/JSSending.swift +++ b/Sources/JavaScriptEventLoop/JSSending.swift @@ -105,7 +105,7 @@ extension JSSending where T == JSObject { construct: { $0 }, deconstruct: { $0 }, getSourceTid: { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) return $0.ownerTid #else _ = $0 @@ -258,7 +258,7 @@ extension JSSending { file: StaticString = #file, line: UInt = #line ) async throws -> T { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) let idInDestination = try await withCheckedThrowingContinuation { continuation in let context = _JSSendingContext(continuation: continuation) let idInSource = self.storage.idInSource @@ -278,8 +278,6 @@ extension JSSending { } #endif - // 6.0 and below can't compile the following without a compiler crash. - #if compiler(>=6.1) /// Receives multiple `JSSending` instances from a thread in a single operation. /// /// This method is more efficient than receiving multiple objects individually, as it @@ -317,7 +315,7 @@ extension JSSending { file: StaticString = #file, line: UInt = #line ) async throws -> (repeat each U) where T == (repeat each U) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) var sendingObjects: [JavaScriptObjectRef] = [] var transferringObjects: [JavaScriptObjectRef] = [] var sourceTid: Int32? @@ -363,7 +361,6 @@ extension JSSending { return try await (repeat (each sendings).receive()) #endif } - #endif // compiler(>=6.1) } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @@ -404,13 +401,11 @@ public struct JSSendingError: Error, CustomStringConvertible { /// - object: The `JSObject` to be received. /// - contextPtr: A pointer to the `_JSSendingContext` instance. // swift-format-ignore -#if compiler(>=6.1) // @_expose and @_extern are only available in Swift 6.1+ @_expose(wasm, "swjs_receive_response") @_cdecl("swjs_receive_response") -#endif @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func _swjs_receive_response(_ object: JavaScriptObjectRef, _ contextPtr: UnsafeRawPointer?) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) guard let contextPtr = contextPtr else { return } let context = Unmanaged<_JSSendingContext>.fromOpaque(contextPtr).takeRetainedValue() context.continuation.resume(returning: object) @@ -424,13 +419,11 @@ func _swjs_receive_response(_ object: JavaScriptObjectRef, _ contextPtr: UnsafeR /// - error: The error to be received. /// - contextPtr: A pointer to the `_JSSendingContext` instance. // swift-format-ignore -#if compiler(>=6.1) // @_expose and @_extern are only available in Swift 6.1+ @_expose(wasm, "swjs_receive_error") @_cdecl("swjs_receive_error") -#endif @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func _swjs_receive_error(_ error: JavaScriptObjectRef, _ contextPtr: UnsafeRawPointer?) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) guard let contextPtr = contextPtr else { return } let context = Unmanaged<_JSSendingContext>.fromOpaque(contextPtr).takeRetainedValue() context.continuation.resume(throwing: JSException(JSObject(id: error).jsValue)) diff --git a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift index 5fc267ddc..ead6157bf 100644 --- a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift +++ b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift @@ -65,7 +65,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable { return _shared } - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) // In multi-threaded environment, we have an event loop executor per // thread (per Web Worker). A job enqueued in one thread should be // executed in the same thread under this global executor. @@ -129,7 +129,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable { _Concurrency._createExecutors(factory: JavaScriptEventLoop.self) } #else - // For Swift 6.1 and below, or Embedded Swift, we need to install + // For Embedded Swift, we need to install // the global executor by hook API. The ExecutorFactory mechanism // does not work in Embedded Swift because ExecutorImpl.swift is // excluded from the embedded Concurrency library. @@ -151,7 +151,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable { } internal func unsafeEnqueue(_ job: UnownedJob) { - #if canImport(wasi_pthread) && compiler(>=6.1) && _runtime(_multithreaded) + #if canImport(wasi_pthread) && _runtime(_multithreaded) guard swjs_get_worker_thread_id_cached() == SWJS_MAIN_THREAD_ID else { // Notify the main thread to execute the job when a job is // enqueued from a Web Worker thread but without an executor preference. diff --git a/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift b/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift index b827ad980..b6bfb9c5f 100644 --- a/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift +++ b/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift @@ -385,7 +385,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor { } func start(timeout: Duration, checkInterval: Duration) async throws { - #if canImport(wasi_pthread) && compiler(>=6.1) && _runtime(_multithreaded) + #if canImport(wasi_pthread) && _runtime(_multithreaded) class Context: @unchecked Sendable { let executor: WebWorkerTaskExecutor.Executor let worker: Worker @@ -610,9 +610,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor { /// Enqueue a job scheduled from a Web Worker thread to the main thread. /// This function is called when a job is enqueued from a Web Worker thread. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -#if compiler(>=6.1) // @_expose and @_extern are only available in Swift 6.1+ @_expose(wasm, "swjs_enqueue_main_job_from_worker") -#endif func _swjs_enqueue_main_job_from_worker(_ job: UnownedJob) { WebWorkerTaskExecutor.traceStatsIncrement(\.receiveJobFromWorkerThread) JavaScriptEventLoop.shared.enqueue(ExecutorJob(job)) @@ -621,9 +619,7 @@ func _swjs_enqueue_main_job_from_worker(_ job: UnownedJob) { /// Wake up the worker thread. /// This function is called when a job is enqueued from the main thread to a worker thread. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -#if compiler(>=6.1) // @_expose and @_extern are only available in Swift 6.1+ @_expose(wasm, "swjs_wake_worker_thread") -#endif func _swjs_wake_worker_thread() { WebWorkerTaskExecutor.Worker.currentThread!.wakeUpFromOtherThread() } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift index 1b6facada..7bc799c64 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift @@ -22,7 +22,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { @usableFromInline internal var _id: JavaScriptObjectRef - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) package let ownerTid: Int32 #endif @@ -35,7 +35,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { @_spi(BridgeJS) public init(id: JavaScriptObjectRef) { self._id = id - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) self.ownerTid = swjs_get_worker_thread_id_cached() #endif } @@ -61,7 +61,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { /// is a programmer error and will result in a runtime assertion failure because JavaScript /// object spaces are not shared across threads backed by Web Workers. private func assertOnOwnerThread(hint: @autoclosure () -> String) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) precondition( ownerTid == swjs_get_worker_thread_id_cached(), "JSObject is being accessed from a thread other than the owner thread: \(hint())" @@ -71,7 +71,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { /// Asserts that the two objects being compared are owned by the same thread. private static func assertSameOwnerThread(lhs: JSObject, rhs: JSObject, hint: @autoclosure () -> String) { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) precondition( lhs.ownerTid == rhs.ownerTid, "JSObject is being accessed from a thread other than the owner thread: \(hint())" @@ -282,7 +282,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { }) deinit { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) if ownerTid != swjs_get_worker_thread_id_cached() { // If the object is not owned by the current thread swjs_release_remote(ownerTid, id) diff --git a/Sources/JavaScriptKit/ThreadLocal.swift b/Sources/JavaScriptKit/ThreadLocal.swift index 12bf78773..4c8bac75f 100644 --- a/Sources/JavaScriptKit/ThreadLocal.swift +++ b/Sources/JavaScriptKit/ThreadLocal.swift @@ -17,7 +17,7 @@ import Glibc /// The value is stored in a thread-local variable, which is a separate copy for each thread. @propertyWrapper final class ThreadLocal: Sendable { - #if compiler(>=6.1) && _runtime(_multithreaded) + #if _runtime(_multithreaded) /// The wrapped value stored in the thread-local storage. /// The initial value is `nil` for each thread. var wrappedValue: Value? { diff --git a/Tests/JavaScriptEventLoopTests/WebWorkerDedicatedExecutorTests.swift b/Tests/JavaScriptEventLoopTests/WebWorkerDedicatedExecutorTests.swift index aae8c2cea..b1dee1f3b 100644 --- a/Tests/JavaScriptEventLoopTests/WebWorkerDedicatedExecutorTests.swift +++ b/Tests/JavaScriptEventLoopTests/WebWorkerDedicatedExecutorTests.swift @@ -1,4 +1,4 @@ -#if compiler(>=6.1) && _runtime(_multithreaded) +#if _runtime(_multithreaded) import XCTest @testable import JavaScriptEventLoop diff --git a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift index 69b3390dc..a40a039bd 100644 --- a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift +++ b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift @@ -1,4 +1,4 @@ -#if compiler(>=6.1) && _runtime(_multithreaded) +#if _runtime(_multithreaded) import Synchronization import XCTest import _CJavaScriptKit // For swjs_get_worker_thread_id From 59fd70248a8cd04e8f1ad4f20ae37909a83cf27b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 22:46:06 +0100 Subject: [PATCH 02/12] fix ci matrix --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7621fad53..8c55a5875 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: toolchain: download-url: https://download.swift.org/swift-6.2-release/ubuntu2204/swift-6.2-RELEASE/swift-6.2-RELEASE-ubuntu22.04.tar.gz wasi-backend: Node - target: "wasm32-unknown-wasi" + target: "wasm32-unknown-wasip1" env: | JAVASCRIPTKIT_DISABLE_TRACING_TRAIT=1 - os: ubuntu-24.04 @@ -108,7 +108,7 @@ jobs: matrix: include: - os: macos-15 - xcode: Xcode_16.4 + xcode: Xcode_26.0.1 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 From 1db28fd56927b4fe1cd4345df1dc737eb756a296 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:06:35 +0100 Subject: [PATCH 03/12] Detach async JS bridge tasks --- Sources/JavaScriptKit/BasicObjects/JSPromise.swift | 3 ++- Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift index c02ab44b0..3a5ef59db 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift @@ -96,7 +96,8 @@ public final class JSPromise: JSBridgedClass { let body: () async throws(JSException) -> JSValue } let context = Context(resolver: resolver, body: body) - Task { + // Detach the task so the JS bridge does not inherit an arbitrary caller executor. + Task.detached { do throws(JSException) { let result = try await context.body() context.resolver(.success(result)) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift index b3e80e66f..db99a6bcb 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift @@ -353,7 +353,7 @@ private func makeAsyncClosure( let body: (sending [JSValue]) async throws(JSException) -> JSValue } let context = Context(resolver: resolver, arguments: arguments, body: body) - Task(priority: priority) { + Task.detached(priority: priority) { do throws(JSException) { let result = try await context.body(context.arguments) context.resolver(.success(result)) From b67cf2468892ad5e7f26233b064088dd7084efe5 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:15:14 +0100 Subject: [PATCH 04/12] Stabilize identity GC test --- Tests/BridgeJSIdentityTests/IdentityModeTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/BridgeJSIdentityTests/IdentityModeTests.swift b/Tests/BridgeJSIdentityTests/IdentityModeTests.swift index 0aa036163..dacf85800 100644 --- a/Tests/BridgeJSIdentityTests/IdentityModeTests.swift +++ b/Tests/BridgeJSIdentityTests/IdentityModeTests.swift @@ -38,7 +38,9 @@ final class IdentityModeTests: XCTestCase { // let FinalizationRegistry fire and call deinit. for _ in 0..<100 { try gc() - try await Task.sleep(for: .milliseconds(0)) + // Give the JS runtime an actual turn so FinalizationRegistry callbacks + // can run before we check the Swift weak reference. + try await Task.sleep(for: .milliseconds(1)) if weakSubject == nil { break } From 16b25456be267a69e36451291850409f4ae3cf27 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:23:50 +0100 Subject: [PATCH 05/12] Revert detached async bridge tasks --- Sources/JavaScriptKit/BasicObjects/JSPromise.swift | 3 +-- Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift index 3a5ef59db..c02ab44b0 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift @@ -96,8 +96,7 @@ public final class JSPromise: JSBridgedClass { let body: () async throws(JSException) -> JSValue } let context = Context(resolver: resolver, body: body) - // Detach the task so the JS bridge does not inherit an arbitrary caller executor. - Task.detached { + Task { do throws(JSException) { let result = try await context.body() context.resolver(.success(result)) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift index db99a6bcb..b3e80e66f 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift @@ -353,7 +353,7 @@ private func makeAsyncClosure( let body: (sending [JSValue]) async throws(JSException) -> JSValue } let context = Context(resolver: resolver, arguments: arguments, body: body) - Task.detached(priority: priority) { + Task(priority: priority) { do throws(JSException) { let result = try await context.body(context.arguments) context.resolver(.success(result)) From 876ba2523a5f7c45ce0a1582a0e0bd1983afbc07 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:34:10 +0100 Subject: [PATCH 06/12] Use Swift 6.2.4 in CI --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c55a5875..6d7fde184 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: entry: - os: ubuntu-22.04 toolchain: - download-url: https://download.swift.org/swift-6.2-release/ubuntu2204/swift-6.2-RELEASE/swift-6.2-RELEASE-ubuntu22.04.tar.gz + download-url: https://download.swift.org/swift-6.2.4-release/ubuntu2204/swift-6.2.4-RELEASE/swift-6.2.4-RELEASE-ubuntu22.04.tar.gz wasi-backend: Node target: "wasm32-unknown-wasip1" env: | @@ -77,7 +77,7 @@ jobs: strategy: matrix: entry: - - image: "swift:6.2" + - image: "swift:6.2.4" swift-syntax-version: "602.0.0" - image: "swift:6.3" swift-syntax-version: "603.0.0" @@ -132,7 +132,7 @@ jobs: format: runs-on: ubuntu-latest container: - image: swift:6.2 + image: swift:6.2.4 steps: - uses: actions/checkout@v6 - run: ./Utilities/format.swift From da54d03f4858bc2352f4c278ab773db9ea0ebc15 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:35:20 +0100 Subject: [PATCH 07/12] Use Swift 6.2.3 in CI --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d7fde184..8a20692a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: entry: - os: ubuntu-22.04 toolchain: - download-url: https://download.swift.org/swift-6.2.4-release/ubuntu2204/swift-6.2.4-RELEASE/swift-6.2.4-RELEASE-ubuntu22.04.tar.gz + download-url: https://download.swift.org/swift-6.2.3-release/ubuntu2204/swift-6.2.3-RELEASE/swift-6.2.3-RELEASE-ubuntu22.04.tar.gz wasi-backend: Node target: "wasm32-unknown-wasip1" env: | @@ -77,7 +77,7 @@ jobs: strategy: matrix: entry: - - image: "swift:6.2.4" + - image: "swift:6.2.3" swift-syntax-version: "602.0.0" - image: "swift:6.3" swift-syntax-version: "603.0.0" @@ -132,7 +132,7 @@ jobs: format: runs-on: ubuntu-latest container: - image: swift:6.2.4 + image: swift:6.2.3 steps: - uses: actions/checkout@v6 - run: ./Utilities/format.swift From b8acd8d6bb157d736f2cd652e437f6fc1eddc2bb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:49:59 +0100 Subject: [PATCH 08/12] Drop Swift 6.2 from CI --- .github/workflows/test.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8a20692a3..a966b5810 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: entry: - os: ubuntu-22.04 toolchain: - download-url: https://download.swift.org/swift-6.2.3-release/ubuntu2204/swift-6.2.3-RELEASE/swift-6.2.3-RELEASE-ubuntu22.04.tar.gz + download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a-ubuntu22.04.tar.gz wasi-backend: Node target: "wasm32-unknown-wasip1" env: | @@ -77,8 +77,6 @@ jobs: strategy: matrix: entry: - - image: "swift:6.2.3" - swift-syntax-version: "602.0.0" - image: "swift:6.3" swift-syntax-version: "603.0.0" runs-on: ubuntu-latest @@ -132,7 +130,7 @@ jobs: format: runs-on: ubuntu-latest container: - image: swift:6.2.3 + image: swift:6.3 steps: - uses: actions/checkout@v6 - run: ./Utilities/format.swift From 4629b0ac1c6430e7de8e0c5b746b51ee661da6ae Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:51:20 +0100 Subject: [PATCH 09/12] Document minimum Swift version --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 03129c3e2..7ec37ab47 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Swift framework to interact with JavaScript through WebAssembly. +Minimum supported Swift toolchain: 6.3. + ## Quick Start Check out the [Hello World](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/tutorials/javascriptkit/hello-world) tutorial for a step-by-step guide to getting started. From e8ec6eec1167d6d08cfcd1e1f1a46a7a72805fe2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:53:05 +0100 Subject: [PATCH 10/12] Add MSSV section to README --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ec37ab47..88c1332a2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ Swift framework to interact with JavaScript through WebAssembly. -Minimum supported Swift toolchain: 6.3. - ## Quick Start Check out the [Hello World](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/tutorials/javascriptkit/hello-world) tutorial for a step-by-step guide to getting started. @@ -122,6 +120,10 @@ Use the [BridgeJS Playground](https://swiftwasm.org/JavaScriptKit/PlayBridgeJS/) Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage patterns. +## Minimum Supported Swift Version (MSSV) + +The minimum supported Swift version is 6.3. + ## Contributing Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the project. From 9c20a1d123051121d40fbd7e71be11d32453dbf6 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 10 Jun 2026 23:55:46 +0100 Subject: [PATCH 11/12] Fix async closure test formatting --- Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift index db093e549..e3c19a8e4 100644 --- a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift +++ b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift @@ -72,7 +72,7 @@ class JSClosureAsyncTests: XCTestCase { )!.value() XCTAssertEqual(result, 42.0) } - + func testAsyncOneshotClosureWithPriority() async throws { let priority = UnsafeSendableBox(nil) let closure = JSOneshotClosure.async(priority: .high) { _ in @@ -83,7 +83,7 @@ class JSClosureAsyncTests: XCTestCase { XCTAssertEqual(result, 42.0) XCTAssertEqual(priority.value, .high) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutor() async throws { let executor = AnyTaskExecutor() @@ -93,7 +93,7 @@ class JSClosureAsyncTests: XCTestCase { let result = try await JSPromise(from: closure.function!())!.value() XCTAssertEqual(result, 42.0) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutorPreference() async throws { let executor = AnyTaskExecutor() From a2a0895569369b093015b87c7a52596bd1f97253 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 11 Jun 2026 00:18:49 +0100 Subject: [PATCH 12/12] Move tracing override to 24.04 matrix entry --- .github/workflows/test.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a966b5810..58b2eb647 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,18 +12,13 @@ jobs: strategy: matrix: entry: - - os: ubuntu-22.04 - toolchain: - download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a-ubuntu22.04.tar.gz - wasi-backend: Node - target: "wasm32-unknown-wasip1" - env: | - JAVASCRIPTKIT_DISABLE_TRACING_TRAIT=1 - os: ubuntu-24.04 toolchain: download-url: https://download.swift.org/development/ubuntu2404/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a/swift-DEVELOPMENT-SNAPSHOT-2025-12-01-a-ubuntu24.04.tar.gz wasi-backend: Node target: "wasm32-unknown-wasip1" + env: | + JAVASCRIPTKIT_DISABLE_TRACING_TRAIT=1 - os: ubuntu-24.04 toolchain: download-url: https://download.swift.org/swift-6.3-branch/ubuntu2404/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a-ubuntu24.04.tar.gz