From 0b8e14a1124c6e6cb7b4b1d799cf62b1c7e344d1 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Thu, 17 Sep 2026 09:58:08 -0600 Subject: [PATCH] fix(swift): drop WasmKit from TraverseEmbedder for Nightly macOS. WasmiHostBridgeClient is the production path; removing WasmKit stops Xcode from compiling SystemExtras against a mismatched swift-system (Stat vs stat). Co-authored-by: Cursor --- .../traverse-embedder-swift/Package.resolved | 69 ------- vendor/traverse-embedder-swift/Package.swift | 9 +- vendor/traverse-embedder-swift/README.md | 21 +-- .../TraverseEmbedder/BridgeClient.swift | 22 +++ .../WasmKitBridgeClient.swift | 175 ------------------ .../WasmKitRuntimeBridge.swift | 120 ------------ 6 files changed, 31 insertions(+), 385 deletions(-) delete mode 100644 vendor/traverse-embedder-swift/Package.resolved create mode 100644 vendor/traverse-embedder-swift/Sources/TraverseEmbedder/BridgeClient.swift delete mode 100644 vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitBridgeClient.swift delete mode 100644 vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitRuntimeBridge.swift diff --git a/vendor/traverse-embedder-swift/Package.resolved b/vendor/traverse-embedder-swift/Package.resolved deleted file mode 100644 index 655a93e..0000000 --- a/vendor/traverse-embedder-swift/Package.resolved +++ /dev/null @@ -1,69 +0,0 @@ -{ - "originHash" : "0bd7e953dbd9000920e63b743fd3dac38352273e9d77e41b2d93d0c57e9be667", - "pins" : [ - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", - "version" : "1.8.2" - } - }, - { - "identity" : "swift-atomics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-atomics.git", - "state" : { - "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", - "version" : "1.3.1" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections.git", - "state" : { - "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", - "version" : "1.6.0" - } - }, - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log", - "state" : { - "revision" : "a878e7f8f46cfc0e1125e565b5c08e7d5272dc9a", - "version" : "1.14.0" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio", - "state" : { - "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b", - "version" : "2.101.3" - } - }, - { - "identity" : "swift-system", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-system", - "state" : { - "revision" : "61e4ca4b81b9e09e2ec863b00c340eb13497dac6", - "version" : "1.5.0" - } - }, - { - "identity" : "wasmkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftwasm/WasmKit.git", - "state" : { - "revision" : "5c389084423c08136040ab8a41f73d4e76fb7b21", - "version" : "0.2.2" - } - } - ], - "version" : 3 -} diff --git a/vendor/traverse-embedder-swift/Package.swift b/vendor/traverse-embedder-swift/Package.swift index d9153af..291275c 100644 --- a/vendor/traverse-embedder-swift/Package.swift +++ b/vendor/traverse-embedder-swift/Package.swift @@ -5,12 +5,7 @@ let package = Package( name: "TraverseEmbedder", platforms: [.iOS(.v17), .macOS(.v14)], products: [.library(name: "TraverseEmbedder", targets: ["TraverseEmbedder"])], - dependencies: [ - .package(url: "https://github.com/swiftwasm/WasmKit.git", exact: "0.2.2"), - // WasmKit 0.2.2 declares `from: 1.5.0`; newer swift-system releases - // collide with its bundled SystemExtras layer on current Xcode. - .package(url: "https://github.com/apple/swift-system.git", exact: "1.5.0"), - ], + dependencies: [], targets: [ .binaryTarget( name: "TraverseSwiftHost", @@ -21,14 +16,12 @@ let package = Package( name: "TraverseEmbedder", dependencies: [ "TraverseSwiftHost", - .product(name: "WasmKit", package: "WasmKit"), ] ), .testTarget( name: "TraverseEmbedderTests", dependencies: [ "TraverseEmbedder", - .product(name: "WAT", package: "WasmKit"), ] ), ] diff --git a/vendor/traverse-embedder-swift/README.md b/vendor/traverse-embedder-swift/README.md index 62c6598..eb1d35a 100644 --- a/vendor/traverse-embedder-swift/README.md +++ b/vendor/traverse-embedder-swift/README.md @@ -8,15 +8,10 @@ the ordered runtime-shaped events recorded by the harness. Compatible-capability start, stop, and kill operations return stable instance identifiers and lifecycle results. It never starts `traverse-cli serve` or uses server-discovery files. -`WasmKitRuntimeBridge` is the production runtime loader. It resolves only the -core WasmKit product, verifies `runtime/runtime.wasm` against its declared -SHA-256 digest and a 32 MiB default artifact limit before parsing, rejects all -ambient imports, and validates the memory, function signatures, and ABI version -required by `runtime-wasm-bridge/1.1.0`, including compatible lifecycle exports. -It never links WasmKitWASI. JSON -marshalling is provided by `WasmKitBridgeClient`, which serializes calls, -copies runtime-owned output before the next mutation, bounds descriptors, and -releases every caller allocation exactly once. +Production execution uses `WasmiHostBridgeClient` backed by the vendored +`TraverseSwiftHost` XCFramework. The client verifies `runtime/runtime.wasm` +against its declared SHA-256 digest and size limits, then invokes the governed +host bridge without linking WasmKit. `RuntimeTraverseEmbedder` maps that boundary into stable public Swift submission, event, and compatible-lifecycle result types without synthesizing @@ -37,10 +32,10 @@ defaults to the package's `TraverseEmbedder.apiVersion`. Initialization rejects a bundle declaring a different version with `incompatibleBundle`; it does not start a sidecar or attempt a network fallback. -The package pins WasmKit 0.2.2 and swift-system 1.5.0 exactly. The accompanying -`dependency-review.json` records why this is the newest reviewed combination -compatible with the package's Swift 6.0 toolchain and discloses the engine's -remaining resource-control limitations. +This package no longer depends on WasmKit or swift-system. Nightly macOS builds +previously failed when SPM resolved a newer swift-system into WasmKit's +`SystemExtras` layer (`Stat` vs `stat`); Wasmi-only keeps the shipping graph +free of that conflict. The package follows semantic versioning. Additive, backward-compatible API changes use minor releases; breaking public API or error-semantic changes use a diff --git a/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/BridgeClient.swift b/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/BridgeClient.swift new file mode 100644 index 0000000..074f753 --- /dev/null +++ b/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/BridgeClient.swift @@ -0,0 +1,22 @@ +import Foundation + +public struct TraverseBridgeError: Error, Equatable, Sendable { + public let status: Int32 + public let message: String + + public init(status: Int32, message: String) { + self.status = status + self.message = message + } +} + +public protocol TraverseBridgeClient: Sendable { + func initialize(configJSON: Data) throws -> Data + func submit(requestJSON: Data) throws -> Data + func cancel(requestJSON: Data) throws -> Data + func compatibleStart(requestJSON: Data) throws -> Data + func compatibleStop(requestJSON: Data) throws -> Data + func compatibleKill(requestJSON: Data) throws -> Data + func nextEvent() throws -> Data? + func shutdown() throws -> Data +} diff --git a/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitBridgeClient.swift b/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitBridgeClient.swift deleted file mode 100644 index ff7c7e1..0000000 --- a/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitBridgeClient.swift +++ /dev/null @@ -1,175 +0,0 @@ -import Foundation -import WasmKit - -/// Serialized UTF-8 JSON client for the governed runtime-WASM bridge. -@available(*, deprecated, message: "Use WasmiHostBridgeClient; WasmKit is retained only for source compatibility.") -public final class WasmKitBridgeClient: @unchecked Sendable, TraverseBridgeClient { - public static let defaultMaximumOutputBytes = 1024 * 1024 - - private let instance: Instance - private let memory: Memory - private let lock = NSLock() - private let maximumOutputBytes: Int - - public init( - bridge: WasmKitRuntimeBridge, - maximumOutputBytes: Int = WasmKitBridgeClient.defaultMaximumOutputBytes - ) throws { - guard maximumOutputBytes > 0 else { - throw TraverseBridgeError(status: -4, message: "maximum bridge output size must be positive") - } - guard let memory = bridge.instance.exports[memory: "memory"] else { - throw TraverseBridgeError(status: -3, message: "bridge_invalid_descriptor") - } - self.instance = bridge.instance - self.memory = memory - self.maximumOutputBytes = maximumOutputBytes - } - - public func initialize(configJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_init", input: configJSON) } - } - - public func submit(requestJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_submit", input: requestJSON) } - } - - public func cancel(requestJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_cancel", input: requestJSON) } - } - - public func compatibleStart(requestJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_compatible_start", input: requestJSON) } - } - - public func compatibleStop(requestJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_compatible_stop", input: requestJSON) } - } - - public func compatibleKill(requestJSON: Data) throws -> Data { - try serialized { try invokeWithInput("traverse_compatible_kill", input: requestJSON) } - } - - public func nextEvent() throws -> Data? { - try serialized { - let descriptor = try allocate(Self.descriptorBytes) - defer { try? deallocate(descriptor, length: Self.descriptorBytes) } - let status = try callStatus("traverse_next_event", [.i32(descriptor)]) - if status == 0 { return nil } - return try readResult(status: status, descriptor: descriptor) - } - } - - public func shutdown() throws -> Data { - try serialized { - let descriptor = try allocate(Self.descriptorBytes) - defer { try? deallocate(descriptor, length: Self.descriptorBytes) } - let status = try callStatus("traverse_shutdown", [.i32(descriptor)]) - return try readResult(status: status, descriptor: descriptor) - } - } - - private func invokeWithInput(_ export: String, input: Data) throws -> Data { - let inputPointer = try allocate(input.count) - let descriptor = try allocate(Self.descriptorBytes) - defer { - try? deallocate(descriptor, length: Self.descriptorBytes) - try? deallocate(inputPointer, length: input.count) - } - try write(input, at: inputPointer) - let status = try callStatus(export, [ - .i32(inputPointer), .i32(UInt32(input.count)), .i32(descriptor), - ]) - return try readResult(status: status, descriptor: descriptor) - } - - private func allocate(_ length: Int) throws -> UInt32 { - guard let function = instance.exports[function: "traverse_alloc"] else { - throw TraverseBridgeError(status: -5, message: "bridge allocation export is unavailable") - } - let result = try function([.i32(UInt32(length))]) - guard result.count == 1, case .i32(let pointer) = result[0] else { - throw TraverseBridgeError(status: -4, message: "bridge allocation failed") - } - return pointer - } - - private func deallocate(_ pointer: UInt32, length: Int) throws { - guard let function = instance.exports[function: "traverse_dealloc"] else { return } - _ = try function([.i32(pointer), .i32(UInt32(length))]) - } - - private func callStatus(_ export: String, _ arguments: [Value]) throws -> Int32 { - guard let function = instance.exports[function: export] else { - throw TraverseBridgeError(status: -5, message: "bridge export \(export) is unavailable") - } - let result = try function(arguments) - guard result.count == 1, case .i32(let rawStatus) = result[0] else { - throw TraverseBridgeError(status: -5, message: "bridge export \(export) returned an invalid status") - } - return Int32(bitPattern: rawStatus) - } - - private func readResult(status: Int32, descriptor: UInt32) throws -> Data { - let descriptorBytes = try read(at: descriptor, count: Self.descriptorBytes) - let pointer = descriptorBytes.prefix(4).withUnsafeBytes { $0.loadUnaligned(as: UInt32.self) }.littleEndian - let length = descriptorBytes.suffix(4).withUnsafeBytes { $0.loadUnaligned(as: UInt32.self) }.littleEndian - guard length <= UInt32(maximumOutputBytes) else { - throw TraverseBridgeError(status: -3, message: "bridge_invalid_descriptor") - } - let output = try read(at: pointer, count: Int(length)) - if status < 0 { - throw TraverseBridgeError( - status: status, - message: String(data: output, encoding: .utf8) ?? "bridge_invalid_json" - ) - } - return output - } - - private func write(_ data: Data, at pointer: UInt32) throws { - guard Int(pointer) <= memory.data.count - data.count else { - throw TraverseBridgeError(status: -3, message: "bridge_invalid_descriptor") - } - _ = memory.withUnsafeMutableBufferPointer(offset: UInt(pointer), count: data.count) { buffer in - data.copyBytes(to: buffer.bindMemory(to: UInt8.self)) - } - } - - private func read(at pointer: UInt32, count: Int) throws -> Data { - let snapshot = memory.data - guard count >= 0, Int(pointer) <= snapshot.count - count else { - throw TraverseBridgeError(status: -3, message: "bridge_invalid_descriptor") - } - return Data(snapshot[Int(pointer)..(_ body: () throws -> T) rethrows -> T { - lock.lock() - defer { lock.unlock() } - return try body() - } - - private static let descriptorBytes = 8 -} - -public struct TraverseBridgeError: Error, Equatable, Sendable { - public let status: Int32 - public let message: String - - public init(status: Int32, message: String) { - self.status = status - self.message = message - } -} - -public protocol TraverseBridgeClient: Sendable { - func initialize(configJSON: Data) throws -> Data - func submit(requestJSON: Data) throws -> Data - func cancel(requestJSON: Data) throws -> Data - func compatibleStart(requestJSON: Data) throws -> Data - func compatibleStop(requestJSON: Data) throws -> Data - func compatibleKill(requestJSON: Data) throws -> Data - func nextEvent() throws -> Data? - func shutdown() throws -> Data -} diff --git a/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitRuntimeBridge.swift b/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitRuntimeBridge.swift deleted file mode 100644 index 0611f5d..0000000 --- a/vendor/traverse-embedder-swift/Sources/TraverseEmbedder/WasmKitRuntimeBridge.swift +++ /dev/null @@ -1,120 +0,0 @@ -import CryptoKit -import Foundation -import WasmKit - -/// Production loader for the governed `runtime-wasm-bridge/1.1.0` module. -/// -/// The loader verifies the artifact before WasmKit parses or instantiates it, -/// rejects ambient imports, and validates the complete embedder export surface. -@available(*, deprecated, message: "Use WasmiHostBridgeClient; WasmKit is retained only for source compatibility.") -public final class WasmKitRuntimeBridge: @unchecked Sendable { - public static let abiVersion = 10_100 - public static let defaultMaximumArtifactBytes = 32 * 1024 * 1024 - - private static let requiredFunctions: [(String, [ValueType], [ValueType])] = [ - ("traverse_bridge_abi_version", [], [.i32]), - ("traverse_alloc", [.i32], [.i32]), - ("traverse_dealloc", [.i32, .i32], []), - ("traverse_init", [.i32, .i32, .i32], [.i32]), - ("traverse_submit", [.i32, .i32, .i32], [.i32]), - ("traverse_next_event", [.i32], [.i32]), - ("traverse_cancel", [.i32, .i32, .i32], [.i32]), - ("traverse_compatible_start", [.i32, .i32, .i32], [.i32]), - ("traverse_compatible_stop", [.i32, .i32, .i32], [.i32]), - ("traverse_compatible_kill", [.i32, .i32, .i32], [.i32]), - ("traverse_shutdown", [.i32], [.i32]), - ] - - private let store: Store - let instance: Instance - - public let runtimeURL: URL - public let runtimeWasmDigest: String - - public init( - bundle: TraverseBundle, - maximumArtifactBytes: Int = WasmKitRuntimeBridge.defaultMaximumArtifactBytes - ) throws { - guard bundle.embedderAPIVersion == TraverseEmbedder.apiVersion else { - throw TraverseEmbedderError.incompatibleBundle( - "embedder API \(bundle.embedderAPIVersion) is incompatible with \(TraverseEmbedder.apiVersion)" - ) - } - guard maximumArtifactBytes > 0 else { - throw TraverseEmbedderError.incompatibleBundle("maximum runtime WASM size must be positive") - } - - let runtimeURL = bundle.rootURL - .appendingPathComponent("runtime", isDirectory: true) - .appendingPathComponent("runtime.wasm", isDirectory: false) - let bytes: Data - do { - bytes = try Data(contentsOf: runtimeURL, options: [.mappedIfSafe]) - } catch { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm is unavailable") - } - guard bytes.count <= maximumArtifactBytes else { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm exceeds the configured size limit") - } - - let actualDigest = "sha256:" + SHA256.hash(data: bytes).map { String(format: "%02x", $0) }.joined() - guard Self.normalizedDigest(bundle.runtimeWasmDigest) == actualDigest else { - throw TraverseEmbedderError.incompatibleBundle("bundle_digest_mismatch") - } - - let module: Module - do { - module = try parseWasm(bytes: Array(bytes)) - } catch { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm is not a valid core WebAssembly module") - } - guard module.imports.isEmpty else { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm requires undeclared ambient imports") - } - - let store = Store(engine: Engine()) - let instance: Instance - do { - instance = try module.instantiate(store: store) - } catch { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm could not be instantiated") - } - let memoryExports = instance.exports.filter { - if case .memory = $0.value { return true } - return false - } - guard memoryExports.count == 1, instance.exports[memory: "memory"] != nil else { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm must export exactly one bridge memory") - } - for (name, parameters, results) in Self.requiredFunctions { - guard let function = instance.exports[function: name] else { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm is missing required export \(name)") - } - guard function.type.parameters == parameters, function.type.results == results else { - throw TraverseEmbedderError.incompatibleBundle("runtime/runtime.wasm has an invalid signature for \(name)") - } - } - - let versionResults: [Value] - do { - versionResults = try instance.exports[function: "traverse_bridge_abi_version"]!() - } catch { - throw TraverseEmbedderError.incompatibleBundle("bridge_version_mismatch") - } - guard versionResults.count == 1, - case .i32(let version) = versionResults[0], - version == UInt32(Self.abiVersion) else { - throw TraverseEmbedderError.incompatibleBundle("bridge_version_mismatch") - } - - self.store = store - self.instance = instance - self.runtimeURL = runtimeURL - self.runtimeWasmDigest = actualDigest - } - - private static func normalizedDigest(_ digest: String) -> String { - let normalized = digest.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() - return normalized.hasPrefix("sha256:") ? normalized : "sha256:" + normalized - } -}