diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69f63a27c..baba9c79b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: target: "wasm32-unknown-wasip1" - os: ubuntu-24.04 toolchain: - download-url: https://download.swift.org/swift-6.3-branch/ubuntu2404/swift-6.3-DEVELOPMENT-SNAPSHOT-2025-12-05-a/swift-6.3-DEVELOPMENT-SNAPSHOT-2025-12-05-a-ubuntu24.04.tar.gz + 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 wasi-backend: Node target: "wasm32-unknown-wasip1" - os: ubuntu-22.04 @@ -81,6 +81,8 @@ jobs: swift-syntax-version: "601.0.0" - image: "swift:6.2" swift-syntax-version: "602.0.0" + - image: "swift:6.3" + swift-syntax-version: "603.0.0" runs-on: ubuntu-latest container: image: ${{ matrix.entry.image }} @@ -167,21 +169,23 @@ jobs: - uses: actions/checkout@v6 - uses: ./.github/actions/install-swift with: - download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2026-02-02-a/swift-DEVELOPMENT-SNAPSHOT-2026-02-02-a-ubuntu22.04.tar.gz + download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2026-03-09-a/swift-DEVELOPMENT-SNAPSHOT-2026-03-09-a-ubuntu22.04.tar.gz - uses: swiftwasm/setup-swiftwasm@v2 id: setup-wasm32-unknown-wasip1 with: { target: wasm32-unknown-wasip1 } - uses: swiftwasm/setup-swiftwasm@v2 id: setup-wasm32-unknown-wasip1-threads with: { target: wasm32-unknown-wasip1-threads } - - run: ./Utilities/build-examples.sh + - run: | + swift --version + ./Utilities/build-examples.sh env: SWIFT_SDK_ID_wasm32_unknown_wasip1_threads: ${{ steps.setup-wasm32-unknown-wasip1-threads.outputs.swift-sdk-id }} SWIFT_SDK_ID_wasm32_unknown_wasip1: ${{ steps.setup-wasm32-unknown-wasip1.outputs.swift-sdk-id }} - run: ./Utilities/prepare-gh-pages.sh - name: Upload static files as artifact id: deployment - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 with: path: ./_site deploy-examples: @@ -197,4 +201,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/Benchmarks/README.md b/Benchmarks/README.md index 65d867eba..3d1a1cf22 100644 --- a/Benchmarks/README.md +++ b/Benchmarks/README.md @@ -4,8 +4,6 @@ This directory contains performance benchmarks for JavaScriptKit. ## Building Benchmarks -Before running the benchmarks, you need to build the test suite: - ```bash swift package --swift-sdk $SWIFT_SDK_ID js -c release ``` @@ -19,19 +17,38 @@ node run.js # Save results to a JSON file node run.js --output=results.json -# Specify number of iterations -node run.js --runs=20 - # Run in adaptive mode until results stabilize node run.js --adaptive --output=stable-results.json -# Run benchmarks and compare with previous results +# Compare with previous results node run.js --baseline=previous-results.json -# Run only a subset of benchmarks -# Substring match +# Filter benchmarks by name node run.js --filter=Call -# Regex (with flags) node run.js --filter=/^Property access\// -node run.js --filter=/string/i ``` + +## Identity Mode Benchmarks + +The benchmark suite includes identity-mode variants (`@JS(identityMode: true)`) of the core classes to measure pointer identity caching. Both variants are in the same build and run as regular benchmarks alongside everything else. + +```bash +# Run only identity benchmarks +node --expose-gc run.js --filter=Identity + +# Run only pointer-mode identity benchmarks +node --expose-gc run.js --filter=Identity/pointer + +# Run only non-identity baseline +node --expose-gc run.js --filter=Identity/none +``` + +### Identity Scenarios + +| Scenario | What it measures | +|----------|-----------------| +| `passBothWaysRoundtrip` | Same object crossing boundary repeatedly (cache hit path) | +| `getPoolRepeated_100` | Bulk return of 100 cached objects (model collection pattern) | +| `churnObjects` | Create, roundtrip, release cycle (FinalizationRegistry cleanup pressure) | +| `swiftConsumesSameObject` | JS passes same object to Swift repeatedly | +| `swiftCreatesObject` | Fresh object creation overhead (cache miss path) | diff --git a/Benchmarks/Sources/Benchmarks.swift b/Benchmarks/Sources/Benchmarks.swift index 59da8c96c..3e24e597b 100644 --- a/Benchmarks/Sources/Benchmarks.swift +++ b/Benchmarks/Sources/Benchmarks.swift @@ -257,6 +257,109 @@ enum ComplexResult { } } +// MARK: - Class Array Performance Tests + +nonisolated(unsafe) var _classArrayPool: [SimpleClass] = [] + +@JS class ClassArrayRoundtrip { + @JS init() {} + + @JS func setupPool(_ count: Int) { + _classArrayPool = (0.. [SimpleClass] { + return _classArrayPool + } + + @JS func makeClassArray() -> [SimpleClass] { + return (0..<100).map { + SimpleClass(name: "Item \($0)", count: $0, flag: true, rate: 0.5, precise: 3.14) + } + } + + @JS func takeClassArray(_ values: [SimpleClass]) {} + + @JS func roundtripClassArray(_ values: [SimpleClass]) -> [SimpleClass] { + return values + } +} + +// MARK: - Identity Cache Benchmark + +nonisolated(unsafe) var _cachedPool: [SimpleClass] = [] + +@JS class IdentityCacheBenchmark { + @JS init() {} + + @JS func setupPool(_ count: Int) { + _cachedPool = (0.. [SimpleClass] { + return _cachedPool + } +} + +// MARK: - Identity Mode Benchmark Variants +// These classes use @JS(identityMode: true) so that identity cache benchmarks +// can run in the SAME build alongside the non-identity classes above. + +@JS(identityMode: true) +class SimpleClassIdentity { + @JS var name: String + @JS var count: Int + @JS var flag: Bool + @JS var rate: Float + @JS var precise: Double + + @JS init(name: String, count: Int, flag: Bool, rate: Float, precise: Double) { + self.name = name + self.count = count + self.flag = flag + self.rate = rate + self.precise = precise + } +} + +@JS(identityMode: true) +class ClassRoundtripIdentity { + @JS init() {} + + @JS func roundtripSimpleClassIdentity(_ obj: SimpleClassIdentity) -> SimpleClassIdentity { + return obj + } + + @JS func makeSimpleClassIdentity() -> SimpleClassIdentity { + return SimpleClassIdentity(name: "Hello", count: 42, flag: true, rate: 0.5, precise: 3.14159) + } + + @JS func takeSimpleClassIdentity(_ obj: SimpleClassIdentity) { + // consume without returning + } +} + +nonisolated(unsafe) var _cachedPoolIdentity: [SimpleClassIdentity] = [] + +@JS(identityMode: true) +class IdentityCacheBenchmarkIdentity { + @JS init() {} + + @JS func setupPool(_ count: Int) { + _cachedPoolIdentity = (0.. [SimpleClassIdentity] { + return _cachedPoolIdentity + } +} + // MARK: - Array Performance Tests @JS struct Point { diff --git a/Benchmarks/Sources/Generated/BridgeJS.Macros.swift b/Benchmarks/Sources/Generated/BridgeJS.Macros.swift index 40fc29e91..b107d8d3c 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.Macros.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.Macros.swift @@ -1,3 +1,4 @@ +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // diff --git a/Benchmarks/Sources/Generated/BridgeJS.swift b/Benchmarks/Sources/Generated/BridgeJS.swift index c9adb2b1a..384ca35a2 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.swift @@ -1,4 +1,5 @@ // bridge-js: skip +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // @@ -1373,6 +1374,448 @@ fileprivate func _bjs_ClassRoundtrip_wrap_extern(_ pointer: UnsafeMutableRawPoin return _bjs_ClassRoundtrip_wrap_extern(pointer) } +@_expose(wasm, "bjs_ClassArrayRoundtrip_init") +@_cdecl("bjs_ClassArrayRoundtrip_init") +public func _bjs_ClassArrayRoundtrip_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ClassArrayRoundtrip() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_setupPool") +@_cdecl("bjs_ClassArrayRoundtrip_setupPool") +public func _bjs_ClassArrayRoundtrip_setupPool(_ _self: UnsafeMutableRawPointer, _ count: Int32) -> Void { + #if arch(wasm32) + ClassArrayRoundtrip.bridgeJSLiftParameter(_self).setupPool(_: Int.bridgeJSLiftParameter(count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_getPool") +@_cdecl("bjs_ClassArrayRoundtrip_getPool") +public func _bjs_ClassArrayRoundtrip_getPool(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ClassArrayRoundtrip.bridgeJSLiftParameter(_self).getPool() + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_makeClassArray") +@_cdecl("bjs_ClassArrayRoundtrip_makeClassArray") +public func _bjs_ClassArrayRoundtrip_makeClassArray(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ClassArrayRoundtrip.bridgeJSLiftParameter(_self).makeClassArray() + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_takeClassArray") +@_cdecl("bjs_ClassArrayRoundtrip_takeClassArray") +public func _bjs_ClassArrayRoundtrip_takeClassArray(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + ClassArrayRoundtrip.bridgeJSLiftParameter(_self).takeClassArray(_: [SimpleClass].bridgeJSStackPop()) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_roundtripClassArray") +@_cdecl("bjs_ClassArrayRoundtrip_roundtripClassArray") +public func _bjs_ClassArrayRoundtrip_roundtripClassArray(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ClassArrayRoundtrip.bridgeJSLiftParameter(_self).roundtripClassArray(_: [SimpleClass].bridgeJSStackPop()) + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassArrayRoundtrip_deinit") +@_cdecl("bjs_ClassArrayRoundtrip_deinit") +public func _bjs_ClassArrayRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension ClassArrayRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_ClassArrayRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_ClassArrayRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "Benchmarks", name: "bjs_ClassArrayRoundtrip_wrap") +fileprivate func _bjs_ClassArrayRoundtrip_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_ClassArrayRoundtrip_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_ClassArrayRoundtrip_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_ClassArrayRoundtrip_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_IdentityCacheBenchmark_init") +@_cdecl("bjs_IdentityCacheBenchmark_init") +public func _bjs_IdentityCacheBenchmark_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = IdentityCacheBenchmark() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmark_setupPool") +@_cdecl("bjs_IdentityCacheBenchmark_setupPool") +public func _bjs_IdentityCacheBenchmark_setupPool(_ _self: UnsafeMutableRawPointer, _ count: Int32) -> Void { + #if arch(wasm32) + IdentityCacheBenchmark.bridgeJSLiftParameter(_self).setupPool(_: Int.bridgeJSLiftParameter(count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmark_getPoolRepeated") +@_cdecl("bjs_IdentityCacheBenchmark_getPoolRepeated") +public func _bjs_IdentityCacheBenchmark_getPoolRepeated(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = IdentityCacheBenchmark.bridgeJSLiftParameter(_self).getPoolRepeated() + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmark_deinit") +@_cdecl("bjs_IdentityCacheBenchmark_deinit") +public func _bjs_IdentityCacheBenchmark_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension IdentityCacheBenchmark: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_IdentityCacheBenchmark_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_IdentityCacheBenchmark_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "Benchmarks", name: "bjs_IdentityCacheBenchmark_wrap") +fileprivate func _bjs_IdentityCacheBenchmark_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_IdentityCacheBenchmark_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_IdentityCacheBenchmark_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_IdentityCacheBenchmark_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_SimpleClassIdentity_init") +@_cdecl("bjs_SimpleClassIdentity_init") +public func _bjs_SimpleClassIdentity_init(_ nameBytes: Int32, _ nameLength: Int32, _ count: Int32, _ flag: Int32, _ rate: Float32, _ precise: Float64) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = SimpleClassIdentity(name: String.bridgeJSLiftParameter(nameBytes, nameLength), count: Int.bridgeJSLiftParameter(count), flag: Bool.bridgeJSLiftParameter(flag), rate: Float.bridgeJSLiftParameter(rate), precise: Double.bridgeJSLiftParameter(precise)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_name_get") +@_cdecl("bjs_SimpleClassIdentity_name_get") +public func _bjs_SimpleClassIdentity_name_get(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = SimpleClassIdentity.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_name_set") +@_cdecl("bjs_SimpleClassIdentity_name_set") +public func _bjs_SimpleClassIdentity_name_set(_ _self: UnsafeMutableRawPointer, _ valueBytes: Int32, _ valueLength: Int32) -> Void { + #if arch(wasm32) + SimpleClassIdentity.bridgeJSLiftParameter(_self).name = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_count_get") +@_cdecl("bjs_SimpleClassIdentity_count_get") +public func _bjs_SimpleClassIdentity_count_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = SimpleClassIdentity.bridgeJSLiftParameter(_self).count + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_count_set") +@_cdecl("bjs_SimpleClassIdentity_count_set") +public func _bjs_SimpleClassIdentity_count_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + SimpleClassIdentity.bridgeJSLiftParameter(_self).count = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_flag_get") +@_cdecl("bjs_SimpleClassIdentity_flag_get") +public func _bjs_SimpleClassIdentity_flag_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = SimpleClassIdentity.bridgeJSLiftParameter(_self).flag + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_flag_set") +@_cdecl("bjs_SimpleClassIdentity_flag_set") +public func _bjs_SimpleClassIdentity_flag_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + SimpleClassIdentity.bridgeJSLiftParameter(_self).flag = Bool.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_rate_get") +@_cdecl("bjs_SimpleClassIdentity_rate_get") +public func _bjs_SimpleClassIdentity_rate_get(_ _self: UnsafeMutableRawPointer) -> Float32 { + #if arch(wasm32) + let ret = SimpleClassIdentity.bridgeJSLiftParameter(_self).rate + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_rate_set") +@_cdecl("bjs_SimpleClassIdentity_rate_set") +public func _bjs_SimpleClassIdentity_rate_set(_ _self: UnsafeMutableRawPointer, _ value: Float32) -> Void { + #if arch(wasm32) + SimpleClassIdentity.bridgeJSLiftParameter(_self).rate = Float.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_precise_get") +@_cdecl("bjs_SimpleClassIdentity_precise_get") +public func _bjs_SimpleClassIdentity_precise_get(_ _self: UnsafeMutableRawPointer) -> Float64 { + #if arch(wasm32) + let ret = SimpleClassIdentity.bridgeJSLiftParameter(_self).precise + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_precise_set") +@_cdecl("bjs_SimpleClassIdentity_precise_set") +public func _bjs_SimpleClassIdentity_precise_set(_ _self: UnsafeMutableRawPointer, _ value: Float64) -> Void { + #if arch(wasm32) + SimpleClassIdentity.bridgeJSLiftParameter(_self).precise = Double.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimpleClassIdentity_deinit") +@_cdecl("bjs_SimpleClassIdentity_deinit") +public func _bjs_SimpleClassIdentity_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension SimpleClassIdentity: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_SimpleClassIdentity_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_SimpleClassIdentity_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "Benchmarks", name: "bjs_SimpleClassIdentity_wrap") +fileprivate func _bjs_SimpleClassIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_SimpleClassIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_SimpleClassIdentity_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_SimpleClassIdentity_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_ClassRoundtripIdentity_init") +@_cdecl("bjs_ClassRoundtripIdentity_init") +public func _bjs_ClassRoundtripIdentity_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ClassRoundtripIdentity() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassRoundtripIdentity_roundtripSimpleClassIdentity") +@_cdecl("bjs_ClassRoundtripIdentity_roundtripSimpleClassIdentity") +public func _bjs_ClassRoundtripIdentity_roundtripSimpleClassIdentity(_ _self: UnsafeMutableRawPointer, _ obj: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ClassRoundtripIdentity.bridgeJSLiftParameter(_self).roundtripSimpleClassIdentity(_: SimpleClassIdentity.bridgeJSLiftParameter(obj)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassRoundtripIdentity_makeSimpleClassIdentity") +@_cdecl("bjs_ClassRoundtripIdentity_makeSimpleClassIdentity") +public func _bjs_ClassRoundtripIdentity_makeSimpleClassIdentity(_ _self: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ClassRoundtripIdentity.bridgeJSLiftParameter(_self).makeSimpleClassIdentity() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassRoundtripIdentity_takeSimpleClassIdentity") +@_cdecl("bjs_ClassRoundtripIdentity_takeSimpleClassIdentity") +public func _bjs_ClassRoundtripIdentity_takeSimpleClassIdentity(_ _self: UnsafeMutableRawPointer, _ obj: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + ClassRoundtripIdentity.bridgeJSLiftParameter(_self).takeSimpleClassIdentity(_: SimpleClassIdentity.bridgeJSLiftParameter(obj)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ClassRoundtripIdentity_deinit") +@_cdecl("bjs_ClassRoundtripIdentity_deinit") +public func _bjs_ClassRoundtripIdentity_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension ClassRoundtripIdentity: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_ClassRoundtripIdentity_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_ClassRoundtripIdentity_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "Benchmarks", name: "bjs_ClassRoundtripIdentity_wrap") +fileprivate func _bjs_ClassRoundtripIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_ClassRoundtripIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_ClassRoundtripIdentity_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_ClassRoundtripIdentity_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_IdentityCacheBenchmarkIdentity_init") +@_cdecl("bjs_IdentityCacheBenchmarkIdentity_init") +public func _bjs_IdentityCacheBenchmarkIdentity_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = IdentityCacheBenchmarkIdentity() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmarkIdentity_setupPool") +@_cdecl("bjs_IdentityCacheBenchmarkIdentity_setupPool") +public func _bjs_IdentityCacheBenchmarkIdentity_setupPool(_ _self: UnsafeMutableRawPointer, _ count: Int32) -> Void { + #if arch(wasm32) + IdentityCacheBenchmarkIdentity.bridgeJSLiftParameter(_self).setupPool(_: Int.bridgeJSLiftParameter(count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmarkIdentity_getPoolRepeated") +@_cdecl("bjs_IdentityCacheBenchmarkIdentity_getPoolRepeated") +public func _bjs_IdentityCacheBenchmarkIdentity_getPoolRepeated(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = IdentityCacheBenchmarkIdentity.bridgeJSLiftParameter(_self).getPoolRepeated() + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityCacheBenchmarkIdentity_deinit") +@_cdecl("bjs_IdentityCacheBenchmarkIdentity_deinit") +public func _bjs_IdentityCacheBenchmarkIdentity_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension IdentityCacheBenchmarkIdentity: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_IdentityCacheBenchmarkIdentity_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_IdentityCacheBenchmarkIdentity_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "Benchmarks", name: "bjs_IdentityCacheBenchmarkIdentity_wrap") +fileprivate func _bjs_IdentityCacheBenchmarkIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_IdentityCacheBenchmarkIdentity_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_IdentityCacheBenchmarkIdentity_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_IdentityCacheBenchmarkIdentity_wrap_extern(pointer) +} + @_expose(wasm, "bjs_ArrayRoundtrip_init") @_cdecl("bjs_ArrayRoundtrip_init") public func _bjs_ArrayRoundtrip_init() -> UnsafeMutableRawPointer { diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json index 95c4ac18e..7209c62f7 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json @@ -1270,6 +1270,506 @@ ], "swiftCallName" : "ClassRoundtrip" }, + { + "constructor" : { + "abiName" : "bjs_ClassArrayRoundtrip_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_ClassArrayRoundtrip_setupPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "setupPool", + "parameters" : [ + { + "label" : "_", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_ClassArrayRoundtrip_getPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getPool", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + }, + { + "abiName" : "bjs_ClassArrayRoundtrip_makeClassArray", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "makeClassArray", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + }, + { + "abiName" : "bjs_ClassArrayRoundtrip_takeClassArray", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeClassArray", + "parameters" : [ + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_ClassArrayRoundtrip_roundtripClassArray", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundtripClassArray", + "parameters" : [ + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + } + ], + "name" : "ClassArrayRoundtrip", + "properties" : [ + + ], + "swiftCallName" : "ClassArrayRoundtrip" + }, + { + "constructor" : { + "abiName" : "bjs_IdentityCacheBenchmark_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_IdentityCacheBenchmark_setupPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "setupPool", + "parameters" : [ + { + "label" : "_", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_IdentityCacheBenchmark_getPoolRepeated", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getPoolRepeated", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClass" + } + } + } + } + } + ], + "name" : "IdentityCacheBenchmark", + "properties" : [ + + ], + "swiftCallName" : "IdentityCacheBenchmark" + }, + { + "constructor" : { + "abiName" : "bjs_SimpleClassIdentity_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "label" : "count", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "label" : "flag", + "name" : "flag", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "rate", + "name" : "rate", + "type" : { + "float" : { + + } + } + }, + { + "label" : "precise", + "name" : "precise", + "type" : { + "double" : { + + } + } + } + ] + }, + "identityMode" : true, + "methods" : [ + + ], + "name" : "SimpleClassIdentity", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "flag", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "rate", + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "precise", + "type" : { + "double" : { + + } + } + } + ], + "swiftCallName" : "SimpleClassIdentity" + }, + { + "constructor" : { + "abiName" : "bjs_ClassRoundtripIdentity_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "identityMode" : true, + "methods" : [ + { + "abiName" : "bjs_ClassRoundtripIdentity_roundtripSimpleClassIdentity", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundtripSimpleClassIdentity", + "parameters" : [ + { + "label" : "_", + "name" : "obj", + "type" : { + "swiftHeapObject" : { + "_0" : "SimpleClassIdentity" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "SimpleClassIdentity" + } + } + }, + { + "abiName" : "bjs_ClassRoundtripIdentity_makeSimpleClassIdentity", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "makeSimpleClassIdentity", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "SimpleClassIdentity" + } + } + }, + { + "abiName" : "bjs_ClassRoundtripIdentity_takeSimpleClassIdentity", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeSimpleClassIdentity", + "parameters" : [ + { + "label" : "_", + "name" : "obj", + "type" : { + "swiftHeapObject" : { + "_0" : "SimpleClassIdentity" + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "ClassRoundtripIdentity", + "properties" : [ + + ], + "swiftCallName" : "ClassRoundtripIdentity" + }, + { + "constructor" : { + "abiName" : "bjs_IdentityCacheBenchmarkIdentity_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "identityMode" : true, + "methods" : [ + { + "abiName" : "bjs_IdentityCacheBenchmarkIdentity_setupPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "setupPool", + "parameters" : [ + { + "label" : "_", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_IdentityCacheBenchmarkIdentity_getPoolRepeated", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getPoolRepeated", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "SimpleClassIdentity" + } + } + } + } + } + ], + "name" : "IdentityCacheBenchmarkIdentity", + "properties" : [ + + ], + "swiftCallName" : "IdentityCacheBenchmarkIdentity" + }, { "constructor" : { "abiName" : "bjs_ArrayRoundtrip_init", @@ -2839,6 +3339,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkHelperNoop", "parameters" : [ @@ -2850,6 +3356,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkHelperNoopWithNumber", "parameters" : [ { @@ -2868,6 +3380,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkRunner", "parameters" : [ { @@ -2900,5 +3418,8 @@ } ] }, - "moduleName" : "Benchmarks" + "moduleName" : "Benchmarks", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Benchmarks/run.js b/Benchmarks/run.js index 5a1ae61e6..444a33be0 100644 --- a/Benchmarks/run.js +++ b/Benchmarks/run.js @@ -282,7 +282,7 @@ async function singleRun(results, nameFilter, iterations) { return; } // Warmup to reduce JIT/IC noise. - body(); + body() if (typeof globalThis.gc === "function") { globalThis.gc(); } @@ -869,6 +869,96 @@ async function singleRun(results, nameFilter, iterations) { arrayRoundtrip.roundtripOptionalArray(null) } }) + + // Identity mode benchmarks - compare classes with and without @JS(identityMode: true) + + // Non-identity baseline (mode = "none") + const classRoundtripNone = new exports.ClassRoundtrip() + const baseObjNone = new exports.SimpleClass('Hello', 42, true, 0.5, 3.14159) + + benchmarkRunner("Identity/none/passBothWaysRoundtrip", () => { + let current = baseObjNone + for (let i = 0; i < iterations; i++) { + current = classRoundtripNone.roundtripSimpleClass(current) + } + }) + + benchmarkRunner("Identity/none/swiftCreatesObject", () => { + for (let i = 0; i < iterations; i++) { + classRoundtripNone.makeSimpleClass() + } + }) + + benchmarkRunner("Identity/none/swiftConsumesSameObject", () => { + for (let i = 0; i < iterations; i++) { + classRoundtripNone.takeSimpleClass(baseObjNone) + } + }) + + benchmarkRunner("Identity/none/churnObjects", () => { + for (let i = 0; i < iterations; i++) { + const obj = new exports.SimpleClass(`temp ${i}`, i, true, 0.5, 3.14159) + classRoundtripNone.roundtripSimpleClass(obj) + obj.release() + } + }) + + const identityCacheNone = new exports.IdentityCacheBenchmark() + identityCacheNone.setupPool(100) + identityCacheNone.getPoolRepeated() // warm the cache + benchmarkRunner("Identity/none/getPoolRepeated_100", () => { + for (let i = 0; i < Math.floor(iterations / 100); i++) { + identityCacheNone.getPoolRepeated() + } + }) + identityCacheNone.release() + + baseObjNone.release() + classRoundtripNone.release() + + // Identity mode (mode = "pointer") + const classRoundtripId = new exports.ClassRoundtripIdentity() + const baseObjId = new exports.SimpleClassIdentity('Hello', 42, true, 0.5, 3.14159) + + benchmarkRunner("Identity/pointer/passBothWaysRoundtrip", () => { + let current = baseObjId + for (let i = 0; i < iterations; i++) { + current = classRoundtripId.roundtripSimpleClassIdentity(current) + } + }) + + benchmarkRunner("Identity/pointer/swiftCreatesObject", () => { + for (let i = 0; i < iterations; i++) { + classRoundtripId.makeSimpleClassIdentity() + } + }) + + benchmarkRunner("Identity/pointer/swiftConsumesSameObject", () => { + for (let i = 0; i < iterations; i++) { + classRoundtripId.takeSimpleClassIdentity(baseObjId) + } + }) + + benchmarkRunner("Identity/pointer/churnObjects", () => { + for (let i = 0; i < iterations; i++) { + const obj = new exports.SimpleClassIdentity(`temp ${i}`, i, true, 0.5, 3.14159) + classRoundtripId.roundtripSimpleClassIdentity(obj) + obj.release() + } + }) + + const identityCacheId = new exports.IdentityCacheBenchmarkIdentity() + identityCacheId.setupPool(100) + identityCacheId.getPoolRepeated() // warm the cache + benchmarkRunner("Identity/pointer/getPoolRepeated_100", () => { + for (let i = 0; i < Math.floor(iterations / 100); i++) { + identityCacheId.getPoolRepeated() + } + }) + identityCacheId.release() + + baseObjId.release() + classRoundtripId.release() } /** @@ -984,7 +1074,7 @@ async function main() { 'min-runs': { type: 'string', default: '5' }, 'max-runs': { type: 'string', default: '50' }, 'target-cv': { type: 'string', default: '5' }, - filter: { type: 'string' } + filter: { type: 'string' }, } }); @@ -1017,7 +1107,7 @@ async function main() { console.log(`Results will be saved to: ${args.values.output}`); } - await runUntilStable(results, options, width, nameFilter, filterArg, iterations); + await runUntilStable(results, options, width, nameFilter, filterArg, iterations) } else { // Fixed number of runs mode const runs = parseInt(args.values.runs, 10); @@ -1039,7 +1129,7 @@ async function main() { console.log("\nOverall Progress:"); for (let i = 0; i < runs; i++) { updateProgress(i, runs, "Benchmark Runs:", width); - await singleRun(results, nameFilter, iterations); + await singleRun(results, nameFilter, iterations) if (i === 0 && Object.keys(results).length === 0) { process.stdout.write("\n"); console.error(`No benchmarks matched filter: ${filterArg}`); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d984555e6..8cb96dec9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,14 +12,37 @@ Thank you for considering contributing to JavaScriptKit! We welcome contribution - Relevant error messages or logs ### Setting Up the Development Environment -1. Clone the repository: - ```bash - git clone https://github.com/swiftwasm/JavaScriptKit.git - cd JavaScriptKit - ``` -2. Install **OSS** Swift toolchain via `swiftly` -3. Install Swift SDK for Wasm corresponding to the Swift version: +Clone the repository: + +```bash +git clone https://github.com/swiftwasm/JavaScriptKit.git +cd JavaScriptKit +``` + +#### Quick start (recommended) + +If you already have an **OSS** Swift toolchain installed via [`swiftly`](https://www.swift.org/install/macos/swiftly), run: + +```bash +./Utilities/setup-dev.sh +``` + +The script verifies prerequisites, installs a matching Wasm Swift SDK from +[swift-sdk-index](https://github.com/swiftwasm/swift-sdk-index), runs `make bootstrap`, +and prints the `SWIFT_SDK_ID` to use with `make unittest`. Re-running it is +idempotent. + +If a `.swift-version` file is present in the repo root, the script will install +that toolchain via `swiftly` automatically. Create one to pin your local dev +toolchain to an indexed release (e.g. `echo 6.3.0 > .swift-version`). The repo +does not track `.swift-version`; if you'd like git to ignore it locally, add it +to `.git/info/exclude`. + +#### Manual setup + +1. Install an **OSS** Swift toolchain via `swiftly`. +2. Install the Swift SDK for Wasm corresponding to the Swift version: ```bash ( set -eo pipefail; \ @@ -35,7 +58,7 @@ Thank you for considering contributing to JavaScriptKit! We welcome contribution jq -r '.["swift-sdks"]["wasm32-unknown-wasip1"]["id"]' ) ``` -4. Install dependencies: +3. Install dependencies: ```bash make bootstrap ``` diff --git a/Examples/ActorOnWebWorker/Package.swift b/Examples/ActorOnWebWorker/Package.swift index 82e87dfdc..04441a9a3 100644 --- a/Examples/ActorOnWebWorker/Package.swift +++ b/Examples/ActorOnWebWorker/Package.swift @@ -6,7 +6,7 @@ let package = Package( name: "Example", platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")], dependencies: [ - .package(path: "../../") + .package(name: "JavaScriptKit", path: "../../") ], targets: [ .executableTarget( diff --git a/Examples/Embedded/Sources/EmbeddedApp/main.swift b/Examples/Embedded/Sources/EmbeddedApp/main.swift index f6bf5b6ac..5e7f01a3c 100644 --- a/Examples/Embedded/Sources/EmbeddedApp/main.swift +++ b/Examples/Embedded/Sources/EmbeddedApp/main.swift @@ -3,7 +3,7 @@ import JavaScriptKit let alert = JSObject.global.alert.object! let document = JSObject.global.document -print("Hello from WASM, document title: \(document.title.string ?? "")") +print("Hello from Wasm, document title: \(document.title.string ?? "")") var count = 0 diff --git a/Examples/MultiModule/Package.swift b/Examples/MultiModule/Package.swift new file mode 100644 index 000000000..870e2a5e8 --- /dev/null +++ b/Examples/MultiModule/Package.swift @@ -0,0 +1,38 @@ +// swift-tools-version:6.0 + +import PackageDescription + +let package = Package( + name: "MultiModule", + platforms: [ + .macOS(.v14) + ], + dependencies: [.package(name: "JavaScriptKit", path: "../../")], + targets: [ + .target( + name: "Core", + dependencies: [ + "JavaScriptKit" + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ], + plugins: [ + .plugin(name: "BridgeJS", package: "JavaScriptKit") + ] + ), + .executableTarget( + name: "MultiModule", + dependencies: [ + "Core", + "JavaScriptKit", + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ], + plugins: [ + .plugin(name: "BridgeJS", package: "JavaScriptKit") + ] + ), + ] +) diff --git a/Examples/MultiModule/README.md b/Examples/MultiModule/README.md new file mode 100644 index 000000000..741394d6f --- /dev/null +++ b/Examples/MultiModule/README.md @@ -0,0 +1,17 @@ +# MultiModule Example + +This example demonstrates using `@JS` types defined in one module (`Core`) from another module (`App`) within the same Swift package. + +## Building and Running + +1. Build the project: + ```sh + swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn + ``` + +2. Serve the files: + ```sh + npx serve + ``` + +Then open your browser to `http://localhost:3000`. diff --git a/Examples/MultiModule/Sources/Core/Vector3D.swift b/Examples/MultiModule/Sources/Core/Vector3D.swift new file mode 100644 index 000000000..988892bcc --- /dev/null +++ b/Examples/MultiModule/Sources/Core/Vector3D.swift @@ -0,0 +1,17 @@ +import JavaScriptKit + +@JS public struct Vector3D { + public let x: Double + public let y: Double + public let z: Double + + @JS public init(x: Double, y: Double, z: Double) { + self.x = x + self.y = y + self.z = z + } + + @JS public func magnitude() -> Double { + (x * x + y * y + z * z).squareRoot() + } +} diff --git a/Examples/MultiModule/Sources/Core/bridge-js.config.json b/Examples/MultiModule/Sources/Core/bridge-js.config.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Examples/MultiModule/Sources/Core/bridge-js.config.json @@ -0,0 +1 @@ +{} diff --git a/Examples/MultiModule/Sources/MultiModule/bridge-js.config.json b/Examples/MultiModule/Sources/MultiModule/bridge-js.config.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Examples/MultiModule/Sources/MultiModule/bridge-js.config.json @@ -0,0 +1 @@ +{} diff --git a/Examples/MultiModule/Sources/MultiModule/main.swift b/Examples/MultiModule/Sources/MultiModule/main.swift new file mode 100644 index 000000000..dd238c00d --- /dev/null +++ b/Examples/MultiModule/Sources/MultiModule/main.swift @@ -0,0 +1,6 @@ +import Core +import JavaScriptKit + +@JS public func currentVelocity() -> Vector3D { + Vector3D(x: 0.1, y: 0.2, z: 0.3) +} diff --git a/Examples/MultiModule/index.html b/Examples/MultiModule/index.html new file mode 100644 index 000000000..d9066820f --- /dev/null +++ b/Examples/MultiModule/index.html @@ -0,0 +1,12 @@ + + + + + MultiModule Example + + + + + + + diff --git a/Examples/MultiModule/index.js b/Examples/MultiModule/index.js new file mode 100644 index 000000000..83e79f16d --- /dev/null +++ b/Examples/MultiModule/index.js @@ -0,0 +1,10 @@ +import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js"; +const { exports } = await init({}); + +const velocity = exports.currentVelocity(); + +const output = document.createElement("pre"); +output.innerText = + `currentVelocity() = (${velocity.x}, ${velocity.y}, ${velocity.z})\n` + + `magnitude = ${velocity.magnitude()}`; +document.body.appendChild(output); diff --git a/Examples/Multithreading/Package.swift b/Examples/Multithreading/Package.swift index 4d1ebde70..ca147c5d8 100644 --- a/Examples/Multithreading/Package.swift +++ b/Examples/Multithreading/Package.swift @@ -6,7 +6,7 @@ let package = Package( name: "Example", platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")], dependencies: [ - .package(path: "../../"), + .package(name: "JavaScriptKit", path: "../../"), .package( url: "https://github.com/kateinoigakukun/chibi-ray", revision: "c8cab621a3338dd2f8e817d3785362409d3b8cf1" diff --git a/Examples/OffscrenCanvas/Package.swift b/Examples/OffscrenCanvas/Package.swift index ca6d7357f..54fa57747 100644 --- a/Examples/OffscrenCanvas/Package.swift +++ b/Examples/OffscrenCanvas/Package.swift @@ -6,7 +6,7 @@ let package = Package( name: "Example", platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")], dependencies: [ - .package(path: "../../") + .package(name: "JavaScriptKit", path: "../../") ], targets: [ .executableTarget( diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.Macros.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.Macros.swift index 4a87a6d38..6c9960b02 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.Macros.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.Macros.swift @@ -1,3 +1,4 @@ +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift index 056c1c27d..920f2cc2f 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift @@ -1,4 +1,5 @@ // bridge-js: skip +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json index 60eb694ff..6f1fc940c 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json @@ -242,6 +242,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createTS2Swift", "parameters" : [ @@ -255,11 +261,18 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "convert", "parameters" : [ { @@ -290,5 +303,8 @@ } ] }, - "moduleName" : "PlayBridgeJS" + "moduleName" : "PlayBridgeJS", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift index ec9eda774..a30eb3b06 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift @@ -45,7 +45,12 @@ import class Foundation.JSONDecoder func _update(swiftSource: String, dtsSource: String) throws -> PlayBridgeJSOutput { let moduleName = "Playground" - let swiftToSkeleton = SwiftToSkeleton(progress: .silent, moduleName: moduleName, exposeToGlobal: false) + let swiftToSkeleton = SwiftToSkeleton( + progress: .silent, + moduleName: moduleName, + exposeToGlobal: false, + externalModuleIndex: .empty + ) swiftToSkeleton.addSourceFile(Parser.parse(source: swiftSource), inputFilePath: "Playground.swift") let ts2swift = try createTS2Swift() diff --git a/Package.swift b/Package.swift index 60adb7a5f..3d0f1e943 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:6.1 +// swift-tools-version:6.2 import CompilerPluginSupport import PackageDescription @@ -7,6 +7,13 @@ import PackageDescription 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 enableTracingByEnv = Context.environment["JAVASCRIPTKIT_ENABLE_TRACING"].flatMap(Bool.init) ?? false + +let tracingTrait = Trait( + name: "Tracing", + description: "Enable opt-in Swift <-> JavaScript bridge tracing hooks.", + enabledTraits: [] +) let testingLinkerFlags: [LinkerSetting] = [ .unsafeFlags( @@ -39,8 +46,9 @@ let package = Package( .plugin(name: "BridgeJS", targets: ["BridgeJS"]), .plugin(name: "BridgeJSCommandPlugin", targets: ["BridgeJSCommandPlugin"]), ], + traits: [tracingTrait], dependencies: [ - .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"603.0.0") + .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"604.0.0") ], targets: [ .target( @@ -53,8 +61,10 @@ let package = Package( .unsafeFlags(["-fdeclspec"]) ] : nil, swiftSettings: [ - .enableExperimentalFeature("Extern") + .enableExperimentalFeature("Extern"), + .define("Tracing", .when(traits: ["Tracing"])), ] + + (enableTracingByEnv ? [.define("Tracing")] : []) + (shouldBuildForEmbedded ? [ .enableExperimentalFeature("Embedded"), @@ -74,8 +84,9 @@ let package = Package( name: "JavaScriptKitTests", dependencies: ["JavaScriptKit"], swiftSettings: [ - .enableExperimentalFeature("Extern") - ], + .enableExperimentalFeature("Extern"), + .define("Tracing", .when(traits: ["Tracing"])), + ] + (enableTracingByEnv ? [.define("Tracing")] : []), linkerSettings: testingLinkerFlags ), @@ -206,5 +217,17 @@ let package = Package( ], linkerSettings: testingLinkerFlags ), + .testTarget( + name: "BridgeJSIdentityTests", + dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], + exclude: [ + "bridge-js.config.json", + "Generated/JavaScript", + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ], + linkerSettings: testingLinkerFlags + ), ] ) diff --git a/Package@swift-6.2.swift b/Package@swift-6.1.swift similarity index 91% rename from Package@swift-6.2.swift rename to Package@swift-6.1.swift index 20583865b..fe98ec529 100644 --- a/Package@swift-6.2.swift +++ b/Package@swift-6.1.swift @@ -1,4 +1,4 @@ -// swift-tools-version:6.2 +// swift-tools-version:6.1 import CompilerPluginSupport import PackageDescription @@ -7,13 +7,6 @@ import PackageDescription 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 enableTracingByEnv = Context.environment["JAVASCRIPTKIT_ENABLE_TRACING"].flatMap(Bool.init) ?? false - -let tracingTrait = Trait( - name: "Tracing", - description: "Enable opt-in Swift <-> JavaScript bridge tracing hooks.", - enabledTraits: [] -) let testingLinkerFlags: [LinkerSetting] = [ .unsafeFlags( @@ -46,7 +39,6 @@ let package = Package( .plugin(name: "BridgeJS", targets: ["BridgeJS"]), .plugin(name: "BridgeJSCommandPlugin", targets: ["BridgeJSCommandPlugin"]), ], - traits: [tracingTrait], dependencies: [ .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"603.0.0") ], @@ -61,10 +53,8 @@ let package = Package( .unsafeFlags(["-fdeclspec"]) ] : nil, swiftSettings: [ - .enableExperimentalFeature("Extern"), - .define("Tracing", .when(traits: ["Tracing"])), + .enableExperimentalFeature("Extern") ] - + (enableTracingByEnv ? [.define("Tracing")] : []) + (shouldBuildForEmbedded ? [ .enableExperimentalFeature("Embedded"), @@ -84,9 +74,8 @@ let package = Package( name: "JavaScriptKitTests", dependencies: ["JavaScriptKit"], swiftSettings: [ - .enableExperimentalFeature("Extern"), - .define("Tracing", .when(traits: ["Tracing"])), - ] + (enableTracingByEnv ? [.define("Tracing")] : []), + .enableExperimentalFeature("Extern") + ], linkerSettings: testingLinkerFlags ), @@ -217,5 +206,17 @@ let package = Package( ], linkerSettings: testingLinkerFlags ), + .testTarget( + name: "BridgeJSIdentityTests", + dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], + exclude: [ + "bridge-js.config.json", + "Generated/JavaScript", + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ], + linkerSettings: testingLinkerFlags + ), ] ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift index 3cb6dc860..b78f5f9a6 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift @@ -63,6 +63,19 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { ]) } + for skeleton in dependencySkeletons(context: context, target: target) { + arguments.append(contentsOf: [ + "--dependency-skeleton", + "\(skeleton.moduleName)=\(skeleton.skeletonURL.path)", + ]) + // We have to use the Swift file, not the skeleton, as the input file, + // since we can’t make the skeleton file an output file without it being + // treated as a resource by the build system (and thus included in the + // resource bundle). We need to use something as the inputFile to maintain + // correct ordering. + inputFiles.append(skeleton.bridgeJSSwiftURL) + } + let allSwiftFiles = inputSwiftFiles + pluginGeneratedSwiftFiles arguments.append(contentsOf: allSwiftFiles.map(\.path)) @@ -74,5 +87,56 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { outputFiles: [outputSwiftPath] ) } + + private struct DependencySkeleton { + let moduleName: String + let skeletonURL: URL + let bridgeJSSwiftURL: URL + } + + /// We only read skeletons from dependencies with a `bridge-js.config.json` file. + /// For the build system to correctly order the plugins, we need to set the skeleton + /// files as input. However, I don’t think we have enough information here to determine + /// whether the plugin which generates this is applied to the dependency, so we use + /// the presence of `bridge-js.config.json` instead. + private func dependencySkeletons( + context: PluginContext, + target: SwiftSourceModuleTarget + ) -> [DependencySkeleton] { + let localTargets: [SwiftSourceModuleTarget] = target.recursiveTargetDependencies + .compactMap { dependency in + guard + let swiftTarget = dependency as? SwiftSourceModuleTarget, + context.package.targets.contains(where: { $0.id == swiftTarget.id }), + FileManager.default.fileExists(atPath: pathToConfigFile(target: swiftTarget).path) + else { + return nil + } + return swiftTarget + } + + var skeletons: [DependencySkeleton] = [] + var seenTargetNames = Set() + for swiftTarget in localTargets where seenTargetNames.insert(swiftTarget.name).inserted { + let skeletonURL = BridgeJSPluginPaths.skeletonURL( + targetName: swiftTarget.name, + packageID: context.package.id, + buildPluginWorkDirectoryURL: context.pluginWorkDirectoryURL + ) + let bridgeJSSwiftURL = BridgeJSPluginPaths.bridgeJSSwiftURL( + targetName: swiftTarget.name, + packageID: context.package.id, + buildPluginWorkDirectoryURL: context.pluginWorkDirectoryURL + ) + skeletons.append( + DependencySkeleton( + moduleName: swiftTarget.name, + skeletonURL: skeletonURL, + bridgeJSSwiftURL: bridgeJSSwiftURL + ) + ) + } + return skeletons + } } #endif diff --git a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSPluginUtilities b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSPluginUtilities new file mode 120000 index 000000000..2daaa446e --- /dev/null +++ b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSPluginUtilities @@ -0,0 +1 @@ +../BridgeJSPluginUtilities \ No newline at end of file diff --git a/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift index 23fbae567..24b96f53d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift @@ -74,28 +74,62 @@ struct BridgeJSCommandPlugin: CommandPlugin { } extension BridgeJSCommandPlugin.Context { - func runOnTargets( - remainingArguments: [String], - where predicate: (SwiftSourceModuleTarget) -> Bool - ) throws { + private func collectBridgeJSTargets() -> [String: SwiftSourceModuleTarget] { + var bridgeJSTargets: [String: SwiftSourceModuleTarget] = [:] for target in context.package.targets { - guard let target = target as? SwiftSourceModuleTarget else { + guard + let swiftTarget = target as? SwiftSourceModuleTarget, + FileManager.default.fileExists( + atPath: swiftTarget.directoryURL.appending(path: "bridge-js.config.json").path + ) + else { continue } - let configFilePath = target.directoryURL.appending(path: "bridge-js.config.json") - if !FileManager.default.fileExists(atPath: configFilePath.path) { - printVerbose("No bridge-js.config.json found for \(target.name), skipping...") - continue + bridgeJSTargets[swiftTarget.name] = swiftTarget + } + return bridgeJSTargets + } + + private func targetsInDependencyOrder( + _ bridgeJSTargets: [String: SwiftSourceModuleTarget] + ) -> [SwiftSourceModuleTarget] { + var visitedTargetNames = Set() + var orderedTargets: [SwiftSourceModuleTarget] = [] + func visit(_ target: SwiftSourceModuleTarget) { + if !visitedTargetNames.insert(target.name).inserted { + return } - guard predicate(target) else { - continue + for dependency in target.recursiveTargetDependencies { + if let dependencyTarget = bridgeJSTargets[dependency.name] { + visit(dependencyTarget) + } } - try runSingleTarget(target: target, remainingArguments: remainingArguments) + orderedTargets.append(target) + } + for target in bridgeJSTargets.values.sorted(by: { $0.name < $1.name }) { + visit(target) + } + return orderedTargets + } + + func runOnTargets( + remainingArguments: [String], + where predicate: (SwiftSourceModuleTarget) -> Bool + ) throws { + let allBridgeJSTargets = collectBridgeJSTargets() + let requestedTargets = allBridgeJSTargets.filter { predicate($1) } + for target in targetsInDependencyOrder(requestedTargets) { + try runSingleTarget( + target: target, + bridgeJSTargets: allBridgeJSTargets, + remainingArguments: remainingArguments + ) } } private func runSingleTarget( target: SwiftSourceModuleTarget, + bridgeJSTargets: [String: SwiftSourceModuleTarget], remainingArguments: [String] ) throws { printStderr("Generating bridge code for \(target.name)...") @@ -126,6 +160,25 @@ extension BridgeJSCommandPlugin.Context { ]) } + for dependency in target.recursiveTargetDependencies { + guard let dependencyTarget = bridgeJSTargets[dependency.name] else { continue } + let dependencySkeletonPath = dependencyTarget.directoryURL + .appending(path: "Generated/JavaScript/BridgeJS.json") + guard FileManager.default.fileExists(atPath: dependencySkeletonPath.path) else { + throw BridgeJSCommandPluginError( + """ + Dependency '\(dependencyTarget.name)' is configured for BridgeJS, but its AOT skeleton has not been generated yet. \ + Run `swift package bridge-js --target \(dependencyTarget.name)` to generate it first, \ + or run without `--target` to process in dependency order. + """ + ) + } + generateArguments.append(contentsOf: [ + "--dependency-skeleton", + "\(dependencyTarget.name)=\(dependencySkeletonPath.path)", + ]) + } + generateArguments.append( contentsOf: target.sourceFiles.filter { !$0.url.path.hasPrefix(generatedDirectory.path + "/") @@ -162,6 +215,14 @@ private func printStderr(_ message: String) { fputs(message + "\n", stderr) } +struct BridgeJSCommandPluginError: Error, CustomStringConvertible { + let description: String + + init(_ message: String) { + self.description = message + } +} + extension SwiftSourceModuleTarget { func hasDependency(named name: String) -> Bool { return dependencies.contains(where: { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index 142050f56..45cfb73f1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -12,13 +12,19 @@ public struct ClosureCodegen { public init() {} private func swiftClosureType(for signature: ClosureSignature) -> String { - let closureParams = signature.parameters.map { "\($0.closureSwiftType)" }.joined(separator: ", ") + let sendingPrefix = signature.sendingParameters ? "sending " : "" + let closureParams = signature.parameters.map { "\(sendingPrefix)\($0.closureSwiftType)" }.joined( + separator: ", " + ) let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "") let swiftReturnType = signature.returnType.closureSwiftType return "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)" } - func renderClosureHelpers(_ signature: ClosureSignature) throws -> [DeclSyntax] { + func renderClosureHelpers( + _ signature: ClosureSignature, + accessLevel: BridgeJSAccessLevel = .internal + ) throws -> [DeclSyntax] { let mangledName = signature.mangleName let helperName = "_BJS_Closure_\(mangledName)" let swiftClosureType = swiftClosureType(for: signature) @@ -29,6 +35,7 @@ public struct ClosureCodegen { let builder = try ImportTS.CallJSEmission( moduleName: "bjs", abiName: externName, + effects: Effects(isAsync: signature.isAsync, isThrows: signature.isThrows), returnType: signature.returnType, context: .exportSwift ) @@ -95,9 +102,10 @@ public struct ClosureCodegen { let helperEnumDecl: DeclSyntax = "\(raw: helperEnumDeclPrinter.lines.joined(separator: "\n"))" + let initAccessModifier = accessLevel.modifierKeyword.map { "\($0) " } ?? "" let typedClosureExtension: DeclSyntax = """ extension JSTypedClosure where Signature == \(raw: swiftClosureType) { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping \(raw: swiftClosureType)) { + \(raw: initAccessModifier)init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping \(raw: swiftClosureType)) { self.init( makeClosure: \(raw: externABIName), body: body, @@ -185,16 +193,16 @@ public struct ClosureCodegen { } public func renderSupport(for skeleton: BridgeJSSkeleton) throws -> String? { - let collector = ClosureSignatureCollectorVisitor() - var walker = BridgeTypeWalker(visitor: collector) + let collector = ClosureSignatureCollectorVisitor(moduleName: skeleton.moduleName) + var walker = BridgeSkeletonWalker(visitor: collector) walker.walk(skeleton) - let closureSignatures = walker.visitor.signatures - - guard !closureSignatures.isEmpty else { return nil } + let signatureAccessLevels = walker.visitor.signatureAccessLevels + guard !signatureAccessLevels.isEmpty else { return nil } var decls: [DeclSyntax] = [] - for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) { - decls.append(contentsOf: try renderClosureHelpers(signature)) + for signature in signatureAccessLevels.keys.sorted(by: { $0.mangleName < $1.mangleName }) { + let accessLevel = signatureAccessLevels[signature] ?? .internal + decls.append(contentsOf: try renderClosureHelpers(signature, accessLevel: accessLevel)) decls.append(try renderClosureInvokeHandler(signature)) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 5b6c7d1ff..b649b244d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -433,7 +433,11 @@ public class ExportSwift { switch self { case .enumStatic(let enumDef): return property.callName(prefix: enumDef.swiftCallName) - case .classStatic, .classInstance: + case .classStatic(let klass): + // property.callName() would use staticContext (the ABI name) as prefix; + // use swiftCallName directly so the emitted expression is valid Swift. + return "\(klass.swiftCallName).\(property.name)" + case .classInstance: return property.callName() case .structStatic(let structDef): return property.callName(prefix: structDef.swiftCallName) @@ -827,15 +831,15 @@ struct StackCodegen { accessor: String, varPrefix: String ) -> [CodeBlockItemSyntax] { - var statements: [CodeBlockItemSyntax] = [] let elemVar = "__bjs_elem_\(varPrefix)" - statements.append("for \(raw: elemVar) in \(raw: accessor) {") - statements.append( - " _swift_js_push_i32((\(raw: elemVar) as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())" - ) - statements.append("}") - statements.append("_swift_js_push_i32(Int32(\(raw: accessor).count))") - return statements + return [ + """ + for \(raw: elemVar) in \(raw: accessor) { + _swift_js_push_i32((\(raw: elemVar) as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } + """, + "_swift_js_push_i32(Int32(\(raw: accessor).count))", + ] } private func lowerDictionaryStatements( @@ -866,49 +870,58 @@ struct StackCodegen { accessor: String, varPrefix: String ) -> [CodeBlockItemSyntax] { - var statements: [CodeBlockItemSyntax] = [] let pairVarName = "__bjs_kv_\(varPrefix)" - statements.append("for \(raw: pairVarName) in \(raw: accessor) {") - statements.append("let __bjs_key_\(raw: varPrefix) = \(raw: pairVarName).key") - statements.append("let __bjs_value_\(raw: varPrefix) = \(raw: pairVarName).value") - - let keyStatements = lowerStatements( - for: .string, - accessor: "__bjs_key_\(varPrefix)", - varPrefix: "\(varPrefix)_key" + let keyVarName = "__bjs_key_\(varPrefix)" + let valueVarName = "__bjs_value_\(varPrefix)" + + // The dispatch in `lowerDictionaryStatements` routes only .nullable and .closure value + // types into this helper, both of which produce single-line lowering statements, so we can + // join their descriptions into the for-body as plain lines without worrying about nested + // multi-statement lowering. + var bodyLines: [String] = [ + "let \(keyVarName) = \(pairVarName).key", + "let \(valueVarName) = \(pairVarName).value", + ] + bodyLines.append( + contentsOf: lowerStatements( + for: .string, + accessor: keyVarName, + varPrefix: "\(varPrefix)_key" + ).map { $0.description } ) - for stmt in keyStatements { - statements.append(stmt) - } - - let valueStatements = lowerStatements( - for: valueType, - accessor: "__bjs_value_\(varPrefix)", - varPrefix: "\(varPrefix)_value" + bodyLines.append( + contentsOf: lowerStatements( + for: valueType, + accessor: valueVarName, + varPrefix: "\(varPrefix)_value" + ).map { $0.description } ) - for stmt in valueStatements { - statements.append(stmt) - } + let body = bodyLines.joined(separator: "\n ") - statements.append("}") - statements.append("_swift_js_push_i32(Int32(\(raw: accessor).count))") - return statements + return [ + """ + for \(raw: pairVarName) in \(raw: accessor) { + \(raw: body) + } + """, + "_swift_js_push_i32(Int32(\(raw: accessor).count))", + ] } private func lowerProtocolDictionaryStatements( accessor: String, varPrefix: String ) -> [CodeBlockItemSyntax] { - var statements: [CodeBlockItemSyntax] = [] let pairVar = "__bjs_kv_\(varPrefix)" - statements.append("for \(raw: pairVar) in \(raw: accessor) {") - statements.append(" \(raw: pairVar).key.bridgeJSStackPush()") - statements.append( - " _swift_js_push_i32((\(raw: pairVar).value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())" - ) - statements.append("}") - statements.append("_swift_js_push_i32(Int32(\(raw: accessor).count))") - return statements + return [ + """ + for \(raw: pairVar) in \(raw: accessor) { + \(raw: pairVar).key.bridgeJSStackPush() + _swift_js_push_i32((\(raw: pairVar).value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } + """, + "_swift_js_push_i32(Int32(\(raw: accessor).count))", + ] } } @@ -953,7 +966,7 @@ struct EnumCodegen { ) printer.nextLine() - printer.write("private init?(bridgeJSRawValue: Int32) {") + printer.write("@_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) {") printer.indent { printer.write("switch bridgeJSRawValue {") for (index, enumCase) in enumDef.cases.enumerated() { @@ -971,7 +984,7 @@ struct EnumCodegen { printer.write("}") printer.nextLine() - printer.write("private var bridgeJSRawValue: Int32 {") + printer.write("@_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 {") printer.indent { printer.write("switch self {") for (index, enumCase) in enumDef.cases.enumerated() { @@ -1073,7 +1086,9 @@ struct EnumCodegen { accessor: paramName, varPrefix: paramName ) - printer.write(multilineString: CodeBlockItemListSyntax(statements).description) + for statement in statements { + printer.write(multilineString: statement.description) + } } } @@ -1207,7 +1222,9 @@ struct StructCodegen { accessor: accessor, varPrefix: property.name ) - printer.write(multilineString: CodeBlockItemListSyntax(statements).description) + for statement in statements { + printer.write(multilineString: statement.description) + } } return printer.lines @@ -1228,6 +1245,7 @@ struct ProtocolCodegen { let builder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: "_extern_\(method.name)", + effects: method.effects, returnType: method.returnType, context: .exportSwift ) @@ -1327,6 +1345,7 @@ struct ProtocolCodegen { let getterBuilder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: getterAbiName, + effects: Effects(isAsync: false, isThrows: false), returnType: property.type, context: .exportSwift ) @@ -1360,6 +1379,7 @@ struct ProtocolCodegen { let setterBuilder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: setterAbiName, + effects: Effects(isAsync: false, isThrows: false), returnType: .void, context: .exportSwift ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExternalModuleIndex.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExternalModuleIndex.swift new file mode 100644 index 000000000..91fd4388a --- /dev/null +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExternalModuleIndex.swift @@ -0,0 +1,93 @@ +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif + +/// Index of `@JS` types from dependencies. +public struct ExternalModuleIndex { + public struct ExternalType: Equatable { + public let moduleName: String + public let bridgeType: BridgeType + } + + public enum LookupResult: Equatable { + case unique(ExternalType) + case ambiguous(candidates: [ExternalType]) + } + + public static var empty: ExternalModuleIndex { + ExternalModuleIndex(dependencies: []) + } + + private let byModuleAndPath: [String: [String: ExternalType]] + private let byPath: [String: [ExternalType]] + + public var moduleNames: Set { Set(byModuleAndPath.keys) } + + public init(dependencies: [(moduleName: String, skeleton: BridgeJSSkeleton)]) { + var entriesByModule: [String: [String: ExternalType]] = [:] + var entriesByDotPath: [String: [ExternalType]] = [:] + + for (moduleName, skeleton) in dependencies { + guard let exported = skeleton.exported else { continue } + var moduleEntries = entriesByModule[moduleName] ?? [:] + + func register(dotPath: String, bridgeType: BridgeType) { + let externalType = ExternalType(moduleName: moduleName, bridgeType: bridgeType) + if moduleEntries[dotPath] == nil { + moduleEntries[dotPath] = externalType + entriesByDotPath[dotPath, default: []].append(externalType) + } + } + + for klass in exported.classes { + register(dotPath: klass.swiftCallName, bridgeType: .swiftHeapObject(klass.swiftCallName)) + } + for structDef in exported.structs { + register(dotPath: structDef.swiftCallName, bridgeType: .swiftStruct(structDef.swiftCallName)) + } + for enumDef in exported.enums { + let bridgeType: BridgeType + switch enumDef.enumType { + case .simple: + bridgeType = .caseEnum(enumDef.swiftCallName) + case .rawValue: + guard let rawType = enumDef.rawType else { continue } + bridgeType = .rawValueEnum(enumDef.swiftCallName, rawType) + case .associatedValue: + bridgeType = .associatedValueEnum(enumDef.swiftCallName) + case .namespace: + bridgeType = .namespaceEnum(enumDef.swiftCallName) + } + register(dotPath: enumDef.swiftCallName, bridgeType: bridgeType) + } + for proto in exported.protocols { + register(dotPath: proto.name, bridgeType: .swiftProtocol(proto.name)) + } + + entriesByModule[moduleName] = moduleEntries + } + + self.byModuleAndPath = entriesByModule + self.byPath = entriesByDotPath + } + + public var isEmpty: Bool { byModuleAndPath.isEmpty } + + public func isKnownModule(_ name: String) -> Bool { + byModuleAndPath[name] != nil + } + + public func lookup(dotPath: String) -> LookupResult? { + guard let matches = byPath[dotPath], !matches.isEmpty else { return nil } + if matches.count == 1 { + return .unique(matches[0]) + } + return .ambiguous(candidates: matches) + } + + public func lookup(dotPath: String, module moduleName: String) -> LookupResult? { + guard let moduleEntries = byModuleAndPath[moduleName] else { return nil } + guard let externalType = moduleEntries[dotPath] else { return nil } + return .unique(externalType) + } +} diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 1d1fe3aa9..02c623918 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -66,9 +66,11 @@ public struct ImportTS { _ getter: ImportedGetterSkeleton, topLevelDecls: inout [DeclSyntax] ) throws -> [DeclSyntax] { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: nil), + effects: effects, returnType: getter.type ) try builder.call() @@ -78,7 +80,8 @@ public struct ImportTS { builder.renderThunkDecl( name: "_$\(getter.name)_get", parameters: [], - returnType: getter.type + returnType: getter.type, + effects: effects ) .with(\.leadingTrivia, Self.renderDocumentation(documentation: getter.documentation)) ] @@ -94,13 +97,14 @@ public struct ImportTS { let abiName: String let moduleName: String + let effects: Effects let returnType: BridgeType let context: BridgeContext var body = CodeFragmentPrinter() var abiParameterForwardings: [String] = [] var abiParameterSignatures: [(name: String, type: WasmCoreType)] = [] - var abiReturnType: WasmCoreType? + let abiReturnType: WasmCoreType? // Track destructured variable names for multiple lowered parameters var destructuredVarNames: [String] = [] // Stack-lowered parameters should be evaluated in reverse order to match LIFO stacks @@ -111,15 +115,31 @@ public struct ImportTS { private var borrowedArguments: [BorrowedArgument] = [] private let needsReturnVariable: Bool - init(moduleName: String, abiName: String, returnType: BridgeType, context: BridgeContext = .importTS) throws { + init( + moduleName: String, + abiName: String, + effects: Effects, + returnType: BridgeType, + context: BridgeContext = .importTS + ) throws { self.moduleName = moduleName self.abiName = abiName + self.effects = effects self.returnType = returnType self.context = context let liftingInfo = try returnType.liftingReturnInfo(context: context) - needsReturnVariable = - !(returnType == .void || returnType.usesSideChannelForOptionalReturn() - || liftingInfo.valueToLift == nil) + if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn() + || liftingInfo.valueToLift == nil + { + abiReturnType = nil + } else { + abiReturnType = liftingInfo.valueToLift + } + needsReturnVariable = abiReturnType != nil + + if effects.isAsync { + prependClosureCallbackParams() + } } func lowerParameter(param: Parameter) throws { @@ -212,6 +232,15 @@ public struct ImportTS { } } + /// Prepends `resolveRef: Int32, rejectRef: Int32` parameters to the ABI parameter list. + /// + /// Used for async imports where the JS side receives closure-backed + /// resolve/reject callbacks as object references. + private func prependClosureCallbackParams() { + abiParameterSignatures.insert(contentsOf: [("resolveRef", .i32), ("rejectRef", .i32)], at: 0) + abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0) + } + func call() throws { for stmt in stackLoweringStmts { body.write(stmt.description) @@ -243,26 +272,32 @@ public struct ImportTS { } } - // Add exception check for ImportTS context - if context == .importTS { + // Add exception check for ImportTS context (skipped for async, where + // errors are funneled through the JS-side reject path) + if !effects.isAsync && context == .importTS { body.write("if let error = _swift_js_take_exception() { throw error }") } } func liftReturnValue() throws { + if effects.isAsync { + liftAsyncReturnValue() + } else { + try liftSyncReturnValue() + } + } + + private func liftSyncReturnValue() throws { let liftingInfo = try returnType.liftingReturnInfo(context: context) if returnType == .void { - abiReturnType = nil return } if returnType.usesSideChannelForOptionalReturn() { // Side channel returns: extern function returns Void, value is retrieved via side channel - abiReturnType = nil body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()") } else { - abiReturnType = liftingInfo.valueToLift let liftExpr: String switch returnType { case .closure(let signature, _): @@ -278,12 +313,38 @@ public struct ImportTS { } } - func assignThis(returnType: BridgeType) { - guard case .jsObject = returnType else { - preconditionFailure("assignThis can only be called with a jsObject return type") + private func liftAsyncReturnValue() { + // For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32` + // and returns void. The JS side calls the resolve/reject closures when the Promise settles. + // The resolve closure is typed to match the return type, so the ABI conversion is handled + // by the existing closure codegen infrastructure — no manual JSValue-to-type switch needed. + + // Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise + let innerBody = body + body = CodeFragmentPrinter() + + let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }" + if returnType == .void { + let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }" + body.write( + "try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" + ) + } else { + let resolveSwiftType = returnType.closureSwiftType + let resolveFactory = + "makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }" + body.write( + "let resolved = try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" + ) + } + body.indent { + body.write(lines: innerBody.lines) + } + body.write("}") + + if returnType != .void { + body.write("return resolved") } - abiReturnType = .i32 - body.write("self.jsObject = JSObject(id: UInt32(bitPattern: ret))") } func renderImportDecl() -> DeclSyntax { @@ -299,9 +360,13 @@ public struct ImportTS { return "\(raw: printer.lines.joined(separator: "\n"))" } - func renderThunkDecl(name: String, parameters: [Parameter], returnType: BridgeType) -> DeclSyntax { + func renderThunkDecl( + name: String, + parameters: [Parameter], + returnType: BridgeType, + effects: Effects + ) -> DeclSyntax { let printer = CodeFragmentPrinter() - let effects = Effects(isAsync: false, isThrows: true) let signature = SwiftSignatureBuilder.buildFunctionSignature( parameters: parameters, returnType: returnType, @@ -362,6 +427,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: function.abiName(context: nil), + effects: function.effects, returnType: function.returnType ) for param in function.parameters { @@ -374,7 +440,8 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(function: function), parameters: function.parameters, - returnType: function.returnType + returnType: function.returnType, + effects: function.effects ) .with(\.leadingTrivia, Self.renderDocumentation(documentation: function.documentation)) ] @@ -388,6 +455,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: method.abiName(context: type), + effects: method.effects, returnType: method.returnType ) try builder.lowerParameter(param: selfParameter) @@ -401,14 +469,20 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type, method: method), parameters: [selfParameter] + method.parameters, - returnType: method.returnType + returnType: method.returnType, + effects: method.effects ) ] } func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { let abiName = method.abiName(context: type, operation: "static") - let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, returnType: method.returnType) + let builder = try CallJSEmission( + moduleName: moduleName, + abiName: abiName, + effects: method.effects, + returnType: method.returnType + ) for param in method.parameters { try builder.lowerParameter(param: param) } @@ -419,15 +493,18 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type, method: method), parameters: method.parameters, - returnType: method.returnType + returnType: method.returnType, + effects: method.effects ) ] } func renderConstructorDecl(constructor: ImportedConstructorSkeleton) throws -> [DeclSyntax] { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: constructor.abiName(context: type), + effects: effects, returnType: .jsObject(nil) ) for param in constructor.parameters { @@ -440,15 +517,18 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type), parameters: constructor.parameters, - returnType: .jsObject(nil) + returnType: .jsObject(nil), + effects: effects ) ] } func renderGetterDecl(getter: ImportedGetterSkeleton) throws -> DeclSyntax { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: type), + effects: effects, returnType: getter.type ) try builder.lowerParameter(param: selfParameter) @@ -459,15 +539,18 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type, propertyName: getter.name, operation: "get"), parameters: [selfParameter], - returnType: getter.type + returnType: getter.type, + effects: effects ) ) } func renderSetterDecl(setter: ImportedSetterSkeleton) throws -> DeclSyntax { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: setter.abiName(context: type), + effects: effects, returnType: .void ) let newValue = Parameter(label: nil, name: "newValue", type: setter.type) @@ -487,7 +570,8 @@ public struct ImportTS { return builder.renderThunkDecl( name: Self.thunkName(type: type, propertyName: propertyNameForThunk, operation: "set"), parameters: [selfParameter, newValue], - returnType: .void + returnType: .void, + effects: effects ) } if let constructor = type.constructor { @@ -844,12 +928,7 @@ extension BridgeType { return LoweringParameterInfo(loweredParameters: [("objectId", .i32)]) } case .caseEnum: - switch context { - case .importTS: - throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LoweringParameterInfo(loweredParameters: [("value", .i32)]) - } + return LoweringParameterInfo(loweredParameters: [("value", .i32)]) case .rawValueEnum(_, let rawType): if rawType == .string { return .string @@ -873,6 +952,10 @@ extension BridgeType { } case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as parameters") + case .nullable(.swiftStruct, _) where context == .importTS: + // Optional `@JS struct`s bridge through the stack (isSome discriminator + fields), + // like optional arrays/dictionaries, rather than the non-optional object-id ABI. + return LoweringParameterInfo(loweredParameters: [("isSome", .i32)]) case .nullable(let wrappedType, _): let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) var params = [("isSome", WasmCoreType.i32)] @@ -923,12 +1006,7 @@ extension BridgeType { return LiftingReturnInfo(valueToLift: .i32) } case .caseEnum: - switch context { - case .importTS: - throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LiftingReturnInfo(valueToLift: .i32) - } + return LiftingReturnInfo(valueToLift: .i32) case .rawValueEnum(_, let rawType): let wasmType = rawType.wasmCoreType ?? .i32 return LiftingReturnInfo(valueToLift: wasmType) @@ -950,6 +1028,14 @@ extension BridgeType { case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as return values") case .nullable(let wrappedType, _): + // jsObject and `@JS struct` use the stack ABI for optionals — the thunk returns + // void and the value (plus isSome discriminator) flows through the stacks. + if case .jsObject = wrappedType { + return LiftingReturnInfo(valueToLift: nil) + } + if case .swiftStruct = wrappedType, context == .importTS { + return LiftingReturnInfo(valueToLift: nil) + } let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) case .array, .dictionary: diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift index 06fb422a9..37040d7a6 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift @@ -342,20 +342,32 @@ public struct BridgeJSConfig: Codable { /// Default: `false` public var exposeToGlobal: Bool - public init(tools: [String: String]? = nil, exposeToGlobal: Bool = false) { + /// The identity mode to use for exported Swift heap objects. + /// + /// When `"pointer"`, Swift heap objects are tracked by pointer identity, + /// enabling identity-based caching. When `"none"` or `nil`, no identity + /// tracking is performed. + /// + /// Default: `nil` (treated as `"none"`) + public var identityMode: String? + + public init(tools: [String: String]? = nil, exposeToGlobal: Bool = false, identityMode: String? = nil) { self.tools = tools self.exposeToGlobal = exposeToGlobal + self.identityMode = identityMode } enum CodingKeys: String, CodingKey { case tools case exposeToGlobal + case identityMode } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) tools = try container.decodeIfPresent([String: String].self, forKey: .tools) exposeToGlobal = try container.decodeIfPresent(Bool.self, forKey: .exposeToGlobal) ?? false + identityMode = try container.decodeIfPresent(String.self, forKey: .identityMode) } /// Load the configuration file from the SwiftPM package target directory. @@ -398,7 +410,8 @@ public struct BridgeJSConfig: Codable { func merging(overrides: BridgeJSConfig) -> BridgeJSConfig { return BridgeJSConfig( tools: (tools ?? [:]).merging(overrides.tools ?? [:], uniquingKeysWith: { $1 }), - exposeToGlobal: overrides.exposeToGlobal + exposeToGlobal: overrides.exposeToGlobal, + identityMode: overrides.identityMode ?? identityMode ) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index 482b1fff5..57b9a57df 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -16,20 +16,40 @@ public final class SwiftToSkeleton { public let progress: ProgressReporting public let moduleName: String public let exposeToGlobal: Bool + public let identityMode: String? - private var sourceFiles: [(sourceFile: SourceFileSyntax, inputFilePath: String)] = [] let typeDeclResolver: TypeDeclResolver + let externalModuleIndex: ExternalModuleIndex - public init(progress: ProgressReporting, moduleName: String, exposeToGlobal: Bool) { + private var sourceFiles: [(sourceFile: SourceFileSyntax, inputFilePath: String)] = [] + private var usedExternalModules = Set() + + public init( + progress: ProgressReporting, + moduleName: String, + exposeToGlobal: Bool, + externalModuleIndex: ExternalModuleIndex, + identityMode: String? = nil + ) { self.progress = progress self.moduleName = moduleName self.exposeToGlobal = exposeToGlobal + self.identityMode = identityMode self.typeDeclResolver = TypeDeclResolver() + self.externalModuleIndex = externalModuleIndex // Index known types provided by JavaScriptKit self.typeDeclResolver.addSourceFile( """ @JSClass struct JSPromise {} + @JSClass struct JSInt8Array {} + @JSClass struct JSUint8Array {} + @JSClass struct JSInt16Array {} + @JSClass struct JSUint16Array {} + @JSClass struct JSInt32Array {} + @JSClass struct JSUint32Array {} + @JSClass struct JSFloat32Array {} + @JSClass struct JSFloat64Array {} """ ) } @@ -42,7 +62,13 @@ public final class SwiftToSkeleton { public func finalize() throws -> BridgeJSSkeleton { var perSourceErrors: [(inputFilePath: String, errors: [DiagnosticError])] = [] var importedFiles: [ImportedFileSkeleton] = [] - var exported = ExportedSkeleton(functions: [], classes: [], enums: [], exposeToGlobal: exposeToGlobal) + var exported = ExportedSkeleton( + functions: [], + classes: [], + enums: [], + exposeToGlobal: exposeToGlobal, + identityMode: identityMode + ) var exportCollectors: [ExportSwiftAPICollector] = [] for (sourceFile, inputFilePath) in sourceFiles { @@ -102,14 +128,43 @@ public final class SwiftToSkeleton { }() let exportedSkeleton: ExportedSkeleton? = exported.isEmpty ? nil : exported - return BridgeJSSkeleton(moduleName: moduleName, exported: exportedSkeleton, imported: importedSkeleton) + return BridgeJSSkeleton( + moduleName: moduleName, + exported: exportedSkeleton, + imported: importedSkeleton, + usedExternalModules: usedExternalModules.sorted() + ) } + private static let jsTypedArrayTypealiasNames: [String: String] = [ + "Int8": "JSInt8Array", + "UInt8": "JSUint8Array", + "Int16": "JSInt16Array", + "UInt16": "JSUint16Array", + "Int32": "JSInt32Array", + "UInt32": "JSUint32Array", + "Float": "JSFloat32Array", + "Float32": "JSFloat32Array", + "Double": "JSFloat64Array", + "Float64": "JSFloat64Array", + ] + func lookupType(for type: TypeSyntax, errors: inout [DiagnosticError]) -> BridgeType? { if let attributedType = type.as(AttributedTypeSyntax.self) { return lookupType(for: attributedType.baseType, errors: &errors) } + // JSTypedArray + if let identifierType = type.as(IdentifierTypeSyntax.self), + identifierType.name.text == "JSTypedArray", + let genericArgs = identifierType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let elementName = genericArgs.first?.argument.as(IdentifierTypeSyntax.self)?.name.text, + let typealiasName = Self.jsTypedArrayTypealiasNames[elementName] + { + return .jsObject(typealiasName) + } + if let identifierType = type.as(IdentifierTypeSyntax.self), identifierType.name.text == "JSTypedClosure", let genericArgs = identifierType.genericArgumentClause?.arguments, @@ -295,79 +350,130 @@ public final class SwiftToSkeleton { return primitiveType } - guard let typeDecl = typeDeclResolver.resolve(type) else { - errors.append( - DiagnosticError( - node: type, - message: "Unsupported type '\(type.trimmedDescription)'.", - hint: "Only primitive types and types defined in the same module are allowed" - ) - ) - return nil - } - - if typeDecl.is(ProtocolDeclSyntax.self) { - let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: typeDecl, itemName: typeDecl.name.text) - return .swiftProtocol(swiftCallName) - } + if let typeDecl = typeDeclResolver.resolve(type) { + if typeDecl.is(ProtocolDeclSyntax.self) { + let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: typeDecl, itemName: typeDecl.name.text) + return .swiftProtocol(swiftCallName) + } - if let enumDecl = typeDecl.as(EnumDeclSyntax.self) { - let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: enumDecl, itemName: enumDecl.name.text) - let rawTypeString = enumDecl.inheritanceClause?.inheritedTypes.first { inheritedType in - let typeName = inheritedType.type.trimmedDescription - return ExportSwiftConstants.supportedRawTypes.contains(typeName) - }?.type.trimmedDescription + if let enumDecl = typeDecl.as(EnumDeclSyntax.self) { + let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: enumDecl, itemName: enumDecl.name.text) + let rawTypeString = enumDecl.inheritanceClause?.inheritedTypes.first { inheritedType in + let typeName = inheritedType.type.trimmedDescription + return ExportSwiftConstants.supportedRawTypes.contains(typeName) + }?.type.trimmedDescription - if let rawType = SwiftEnumRawType(rawTypeString) { - return .rawValueEnum(swiftCallName, rawType) - } else { - let hasAnyCases = enumDecl.memberBlock.members.contains { member in - member.decl.is(EnumCaseDeclSyntax.self) - } - if !hasAnyCases { - return .namespaceEnum(swiftCallName) - } - let hasAssociatedValues = - enumDecl.memberBlock.members.contains { member in - guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { return false } - return caseDecl.elements.contains { element in - if let params = element.parameterClause?.parameters { - return !params.isEmpty + if let rawType = SwiftEnumRawType(rawTypeString) { + return .rawValueEnum(swiftCallName, rawType) + } else { + let hasAnyCases = enumDecl.memberBlock.members.contains { member in + member.decl.is(EnumCaseDeclSyntax.self) + } + if !hasAnyCases { + return .namespaceEnum(swiftCallName) + } + let hasAssociatedValues = + enumDecl.memberBlock.members.contains { member in + guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { return false } + return caseDecl.elements.contains { element in + if let params = element.parameterClause?.parameters { + return !params.isEmpty + } + return false } - return false } + if hasAssociatedValues { + return .associatedValueEnum(swiftCallName) + } else { + return .caseEnum(swiftCallName) } - if hasAssociatedValues { - return .associatedValueEnum(swiftCallName) - } else { - return .caseEnum(swiftCallName) } } - } - if let structDecl = typeDecl.as(StructDeclSyntax.self) { - let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: structDecl, itemName: structDecl.name.text) - if structDecl.attributes.hasAttribute(name: "JSClass") { + if let structDecl = typeDecl.as(StructDeclSyntax.self) { + let swiftCallName = SwiftToSkeleton.computeSwiftCallName( + for: structDecl, + itemName: structDecl.name.text + ) + if structDecl.attributes.hasAttribute(name: "JSClass") { + return .jsObject(swiftCallName) + } + return .swiftStruct(swiftCallName) + } + + guard typeDecl.is(ClassDeclSyntax.self) || typeDecl.is(ActorDeclSyntax.self) else { + return nil + } + let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: typeDecl, itemName: typeDecl.name.text) + + // A type annotated with @JSClass is a JavaScript object wrapper (imported), + // even if it is declared as a Swift class. + if let classDecl = typeDecl.as(ClassDeclSyntax.self), classDecl.attributes.hasAttribute(name: "JSClass") { + return .jsObject(swiftCallName) + } + if let actorDecl = typeDecl.as(ActorDeclSyntax.self), actorDecl.attributes.hasAttribute(name: "JSClass") { return .jsObject(swiftCallName) } - return .swiftStruct(swiftCallName) + + return .swiftHeapObject(swiftCallName) + } + + if let externalType = resolveExternal(for: type, errors: &errors) { + return externalType } - guard typeDecl.is(ClassDeclSyntax.self) || typeDecl.is(ActorDeclSyntax.self) else { + errors.append( + DiagnosticError( + node: type, + message: "Unsupported type '\(type.trimmedDescription)'.", + hint: + "Only primitive types, types defined in the same module, and " + + "`@JS` types from dependency targets that apply the BridgeJS plugin are allowed" + ) + ) + return nil + } + + private func resolveExternal(for type: TypeSyntax, errors: inout [DiagnosticError]) -> BridgeType? { + guard + !externalModuleIndex.isEmpty, + var components = typeDeclResolver.qualifiedComponents(from: type) + else { return nil } - let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: typeDecl, itemName: typeDecl.name.text) - // A type annotated with @JSClass is a JavaScript object wrapper (imported), - // even if it is declared as a Swift class. - if let classDecl = typeDecl.as(ClassDeclSyntax.self), classDecl.attributes.hasAttribute(name: "JSClass") { - return .jsObject(swiftCallName) + var scopedModule: String? = nil + if components.count >= 2, externalModuleIndex.isKnownModule(components[0]) { + scopedModule = components[0] + components.removeFirst() } - if let actorDecl = typeDecl.as(ActorDeclSyntax.self), actorDecl.attributes.hasAttribute(name: "JSClass") { - return .jsObject(swiftCallName) + + let dotPath = components.joined(separator: ".") + let lookupResult: ExternalModuleIndex.LookupResult? + if let moduleName = scopedModule { + lookupResult = externalModuleIndex.lookup(dotPath: dotPath, module: moduleName) + } else { + lookupResult = externalModuleIndex.lookup(dotPath: dotPath) } - return .swiftHeapObject(swiftCallName) + guard let lookupResult else { return nil } + switch lookupResult { + case .unique(let externalType): + usedExternalModules.insert(externalType.moduleName) + return externalType.bridgeType + case .ambiguous(let candidates): + let moduleNames = candidates.map(\.moduleName).sorted().joined(separator: ", ") + errors.append( + DiagnosticError( + node: type, + message: "ambiguous use of '\(type.trimmedDescription)'", + hint: + "'\(dotPath)' is exported by multiple dependency modules: \(moduleNames). " + + "Qualify with a module name (e.g. '.\(dotPath)') to disambiguate." + ) + ) + return nil + } } fileprivate static func parseUnsafePointerType(_ type: TypeSyntax) -> UnsafePointerType? { @@ -430,6 +536,14 @@ public final class SwiftToSkeleton { enumDecl.attributes.hasJSAttribute() { swiftPath.insert(enumDecl.name.text, at: 0) + } else if let structDecl = parent.as(StructDeclSyntax.self), + structDecl.attributes.hasJSAttribute() + { + swiftPath.insert(structDecl.name.text, at: 0) + } else if let classDecl = parent.as(ClassDeclSyntax.self), + classDecl.attributes.hasJSAttribute() + { + swiftPath.insert(classDecl.name.text, at: 0) } currentNode = parent.parent } @@ -574,6 +688,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { var state: State { return stateStack.current } + let parent: SwiftToSkeleton init(parent: SwiftToSkeleton) { @@ -1189,6 +1304,14 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { return nil } + private func extractIdentityMode(from jsAttribute: AttributeSyntax) -> Bool? { + guard let arguments = jsAttribute.arguments?.as(LabeledExprListSyntax.self), + let identityArg = arguments.first(where: { $0.label?.text == "identityMode" }) + else { return nil } + let text = identityArg.expression.trimmedDescription + return text == "true" + } + override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { guard let jsAttribute = node.attributes.firstJSAttribute else { return .skipChildren } @@ -1371,11 +1494,16 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { guard namespaceResult.isValid else { return .skipChildren } + let effectiveNamespace = effectiveNamespace( + resolvedNamespace: namespaceResult.namespace, + parentTypeNamespace: computeParentTypeNamespace(for: node) + ) let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: node, itemName: name) let explicitAccessControl = computeExplicitAtLeastInternalAccessControl( for: node, message: "Class visibility must be at least internal" ) + let classIdentityMode = extractIdentityMode(from: jsAttribute) let exportedClass = ExportedClass( name: name, swiftCallName: swiftCallName, @@ -1383,9 +1511,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { constructor: nil, methods: [], properties: [], - namespace: namespaceResult.namespace + namespace: effectiveNamespace, + identityMode: classIdentityMode ) - let uniqueKey = makeKey(name: name, namespace: namespaceResult.namespace) + let uniqueKey = makeKey(name: name, namespace: effectiveNamespace) stateStack.push(state: .classBody(name: name, key: uniqueKey)) exportedClassByName[uniqueKey] = exportedClass @@ -1474,6 +1603,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { guard namespaceResult.isValid else { return .skipChildren } + let effectiveNamespace = effectiveNamespace( + resolvedNamespace: namespaceResult.namespace, + parentTypeNamespace: computeParentTypeNamespace(for: node) + ) let emitStyle = extractEnumStyle(from: jsAttribute) ?? .const let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: node, itemName: name) let explicitAccessControl = computeExplicitAtLeastInternalAccessControl( @@ -1482,7 +1615,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { ) let tsFullPath: String - if let namespace = namespaceResult.namespace, !namespace.isEmpty { + if let namespace = effectiveNamespace, !namespace.isEmpty { tsFullPath = namespace.joined(separator: ".") + "." + name } else { tsFullPath = name @@ -1496,13 +1629,13 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { explicitAccessControl: explicitAccessControl, cases: [], // Will be populated in visit(EnumCaseDeclSyntax) rawType: SwiftEnumRawType(rawType), - namespace: namespaceResult.namespace, + namespace: effectiveNamespace, emitStyle: emitStyle, staticMethods: [], staticProperties: [] ) - let enumUniqueKey = makeKey(name: name, namespace: namespaceResult.namespace) + let enumUniqueKey = makeKey(name: name, namespace: effectiveNamespace) exportedEnumByName[enumUniqueKey] = exportedEnum exportedEnumNames.append(enumUniqueKey) @@ -1601,18 +1734,22 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { guard namespaceResult.isValid else { return .skipChildren } + let effectiveNamespace = effectiveNamespace( + resolvedNamespace: namespaceResult.namespace, + parentTypeNamespace: computeParentTypeNamespace(for: node) + ) _ = computeExplicitAtLeastInternalAccessControl( for: node, message: "Protocol visibility must be at least internal" ) - let protocolUniqueKey = makeKey(name: name, namespace: namespaceResult.namespace) + let protocolUniqueKey = makeKey(name: name, namespace: effectiveNamespace) exportedProtocolByName[protocolUniqueKey] = ExportedProtocol( name: name, methods: [], properties: [], - namespace: namespaceResult.namespace + namespace: effectiveNamespace ) stateStack.push(state: .protocolBody(name: name, key: protocolUniqueKey)) @@ -1623,7 +1760,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { if let exportedFunction = visitProtocolMethod( node: funcDecl, protocolName: name, - namespace: namespaceResult.namespace + namespace: effectiveNamespace ) { methods.append(exportedFunction) } @@ -1636,7 +1773,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { name: name, methods: methods, properties: exportedProtocolByName[protocolUniqueKey]?.properties ?? [], - namespace: namespaceResult.namespace + namespace: effectiveNamespace ) exportedProtocolByName[protocolUniqueKey] = exportedProtocol @@ -1658,6 +1795,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { guard namespaceResult.isValid else { return .skipChildren } + let effectiveNamespace = effectiveNamespace( + resolvedNamespace: namespaceResult.namespace, + parentTypeNamespace: computeParentTypeNamespace(for: node) + ) let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: node, itemName: name) let explicitAccessControl = computeExplicitAtLeastInternalAccessControl( for: node, @@ -1707,7 +1848,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { type: fieldType, isReadonly: true, isStatic: false, - namespace: namespaceResult.namespace, + namespace: effectiveNamespace, staticContext: nil ) properties.append(property) @@ -1715,14 +1856,14 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { } } - let structUniqueKey = makeKey(name: name, namespace: namespaceResult.namespace) + let structUniqueKey = makeKey(name: name, namespace: effectiveNamespace) let exportedStruct = ExportedStruct( name: name, swiftCallName: swiftCallName, explicitAccessControl: explicitAccessControl, properties: properties, methods: [], - namespace: namespaceResult.namespace + namespace: effectiveNamespace ) exportedStructByName[structUniqueKey] = exportedStruct @@ -1734,11 +1875,46 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { } override func visitPost(_ node: StructDeclSyntax) { - if case .structBody(_, _) = stateStack.current { + if case .structBody(_, let structKey) = stateStack.current { stateStack.pop() + validateStructInitOrder(node: node, structKey: structKey) } } + private func validateStructInitOrder(node: StructDeclSyntax, structKey: String) { + guard let exportedStruct = exportedStructByName[structKey], + let constructor = exportedStruct.constructor + else { + // No explicit @JS init — synthesized memberwise init is assumed, + // which always matches declaration order. + return + } + + let instanceProps = exportedStruct.properties.filter { !$0.isStatic } + let expectedLabels = instanceProps.map(\.name) + let actualLabels = constructor.parameters.compactMap(\.label) + + guard expectedLabels != actualLabels else { return } + + // Find the @JS init node so we can point the diagnostic at it. + let initNode: (any SyntaxProtocol) = + node.memberBlock.members + .compactMap { $0.decl.as(InitializerDeclSyntax.self) } + .first(where: { $0.attributes.hasJSAttribute() }) + ?? node + + let expectedOrder = expectedLabels.joined(separator: ", ") + let actualOrder = actualLabels.joined(separator: ", ") + + diagnose( + node: initNode, + message: + "@JS struct initializer parameters must match stored properties in declaration order. Expected (\(expectedOrder)), got (\(actualOrder))", + hint: + "Reorder the initializer parameters to match the property declaration order, or remove the @JS init to use the synthesized memberwise initializer" + ) + } + private func visitProtocolMethod( node: FunctionDeclSyntax, protocolName: String, @@ -1951,6 +2127,34 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { return namespace.isEmpty ? nil : namespace } + private func computeParentTypeNamespace(for node: some SyntaxProtocol) -> [String]? { + var path: [String] = [] + var currentNode: Syntax? = node.parent + + while let parent = currentNode { + if let structDecl = parent.as(StructDeclSyntax.self), + structDecl.attributes.hasJSAttribute() + { + path.insert(structDecl.name.text, at: 0) + } else if let classDecl = parent.as(ClassDeclSyntax.self), + classDecl.attributes.hasJSAttribute() + { + path.insert(classDecl.name.text, at: 0) + } + currentNode = parent.parent + } + + return path.isEmpty ? nil : path + } + + private func effectiveNamespace( + resolvedNamespace: [String]?, + parentTypeNamespace: [String]? + ) -> [String]? { + let combined = (parentTypeNamespace ?? []) + (resolvedNamespace ?? []) + return combined.isEmpty ? nil : combined + } + /// Requires the node to have at least internal access control. private func computeExplicitAtLeastInternalAccessControl( for node: some WithModifiersSyntax, @@ -2038,6 +2242,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let name: String let jsName: String? let from: JSImportFrom? + let accessLevel: BridgeJSAccessLevel var constructor: ImportedConstructorSkeleton? var methods: [ImportedFunctionSkeleton] var staticMethods: [ImportedFunctionSkeleton] @@ -2137,7 +2342,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let valueType: BridgeType } - /// Validates effects (throws required, async not supported) + /// Validates effects (throws required, async only supported for @JSFunction) private func validateEffects( _ effects: FunctionEffectSpecifiersSyntax?, node: some SyntaxProtocol, @@ -2153,7 +2358,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { ) return nil } - if effects.isAsync { + if effects.isAsync && attributeName != "JSFunction" { errors.append( DiagnosticError( node: node, @@ -2253,6 +2458,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { name: typeName, jsName: nil, from: nil, + accessLevel: .internal, constructor: nil, methods: [], staticMethods: [], @@ -2261,12 +2467,18 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { ) } - private func enterJSClass(_ typeName: String, jsName: String?, from: JSImportFrom?) { + private func enterJSClass( + _ typeName: String, + jsName: String?, + from: JSImportFrom?, + accessLevel: BridgeJSAccessLevel + ) { stateStack.append(.jsClassBody(name: typeName)) currentType = CurrentType( name: typeName, jsName: jsName, from: from, + accessLevel: accessLevel, constructor: nil, methods: [], staticMethods: [], @@ -2287,7 +2499,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { staticMethods: type.staticMethods, getters: type.getters, setters: type.setters, - documentation: nil + documentation: nil, + accessLevel: type.accessLevel ) ) currentType = nil @@ -2300,7 +2513,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let attribute = AttributeChecker.firstJSClassAttribute(node.attributes) let jsName = attribute.flatMap(AttributeChecker.extractJSName) let from = attribute.flatMap(AttributeChecker.extractJSImportFrom) - enterJSClass(node.name.text, jsName: jsName, from: from) + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) + enterJSClass(node.name.text, jsName: jsName, from: from, accessLevel: accessLevel) } return .visitChildren } @@ -2316,7 +2530,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let attribute = AttributeChecker.firstJSClassAttribute(node.attributes) let jsName = attribute.flatMap(AttributeChecker.extractJSName) let from = attribute.flatMap(AttributeChecker.extractJSImportFrom) - enterJSClass(node.name.text, jsName: jsName, from: from) + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) + enterJSClass(node.name.text, jsName: jsName, from: from, accessLevel: accessLevel) } return .visitChildren } @@ -2481,8 +2696,14 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { else { return nil } + // Initializers without an explicit modifier inherit access from the + // enclosing `@JSClass` (the user's example pattern: `public init(...)` + // inside `public struct JSDocument`). + let parentLevel = currentType?.accessLevel ?? .internal + let accessLevel = Self.bridgeAccessLevel(from: initializer.modifiers, default: parentLevel) return ImportedConstructorSkeleton( - parameters: parseParameters(from: initializer.signature.parameterClause) + parameters: parseParameters(from: initializer.signature.parameterClause), + accessLevel: accessLevel ) } @@ -2490,7 +2711,12 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { _ jsFunction: AttributeSyntax, _ node: FunctionDeclSyntax, ) -> ImportedFunctionSkeleton? { - guard validateEffects(node.signature.effectSpecifiers, node: node, attributeName: "JSFunction") != nil + guard + let effects = validateEffects( + node.signature.effectSpecifiers, + node: node, + attributeName: "JSFunction" + ) else { return nil } @@ -2510,13 +2736,16 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { } else { returnType = .void } + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) return ImportedFunctionSkeleton( name: name, jsName: jsName, from: from, parameters: parameters, returnType: returnType, - documentation: nil + effects: effects, + documentation: nil, + accessLevel: accessLevel ) } @@ -2548,13 +2777,15 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let propertyName = SwiftToSkeleton.normalizeIdentifier(identifier.identifier.text) let jsName = AttributeChecker.extractJSName(from: jsGetter) let from = AttributeChecker.extractJSImportFrom(from: jsGetter) + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) return ImportedGetterSkeleton( name: propertyName, jsName: jsName, from: from, type: propertyType, documentation: nil, - functionName: nil + functionName: nil, + accessLevel: accessLevel ) } @@ -2577,12 +2808,14 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { return nil } + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) return ImportedSetterSkeleton( name: propertyName, jsName: validation.jsName, type: validation.valueType, documentation: nil, - functionName: "\(functionBaseName)_set" + functionName: "\(functionBaseName)_set", + accessLevel: accessLevel ) } @@ -2628,6 +2861,31 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { modifier.name.tokenKind == .keyword(.static) || modifier.name.tokenKind == .keyword(.class) } } + + /// Maps Swift's declaration modifiers to a `BridgeJSAccessLevel` for + /// recording on imported skeleton entries. Falls back to `default` when no + /// access modifier is present (typically `.internal`, but the caller may + /// override — e.g. an `init` inheriting from its enclosing `@JSClass`). + /// `private`/`fileprivate` are mapped to the fallback because the macros + /// already reject those access levels for `@JS*` declarations. + fileprivate static func bridgeAccessLevel( + from modifiers: DeclModifierListSyntax, + default fallback: BridgeJSAccessLevel = .internal + ) -> BridgeJSAccessLevel { + for modifier in modifiers { + switch modifier.name.tokenKind { + case .keyword(.public), .keyword(.open): + return .public + case .keyword(.package): + return .package + case .keyword(.internal): + return .internal + default: + continue + } + } + return fallback + } } extension GenericArgumentListSyntax { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift index 975c0c9dc..ec04421aa 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift @@ -161,7 +161,7 @@ class TypeDeclResolver { return nil } - private func qualifiedComponents(from type: TypeSyntax) -> QualifiedName? { + func qualifiedComponents(from type: TypeSyntax) -> QualifiedName? { if let m = type.as(MemberTypeSyntax.self) { guard let base = qualifiedComponents(from: TypeSyntax(m.baseType)) else { return nil } return base + [m.name.text] diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index f69a4b266..ce0ba0cb8 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -25,6 +25,21 @@ public struct BridgeJSLink { self.sharedMemory = sharedMemory } + /// The identity mode from the config file, resolved from skeletons. + var configIdentityMode: String { + skeletons.compactMap(\.exported).compactMap(\.identityMode).first ?? "none" + } + + /// Whether a class should use identity caching based on its annotation and the config default. + private func shouldUseIdentityCache(for klass: ExportedClass) -> Bool { + // Per-class annotation takes priority + if let classOverride = klass.identityMode { + return classOverride + } + // Fall back to config default + return configIdentityMode == "pointer" + } + mutating func addSkeletonFile(data: Data) throws { do { let unified = try JSONDecoder().decode(BridgeJSSkeleton.self, from: data) @@ -85,31 +100,52 @@ public struct BridgeJSLink { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; """ if enableLifetimeTracking { - output += " TRACKING.wrap(pointer, deinit, prototype, state);\n" + output += " TRACKING.wrap(pointer, deinit, prototype, state);\n" } output += """ - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { """ if enableLifetimeTracking { - output += " TRACKING.release(this);\n" + output += " TRACKING.release(this);\n" } output += """ const state = this.__swiftHeapObjectState; @@ -118,6 +154,7 @@ public struct BridgeJSLink { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } @@ -130,6 +167,7 @@ public struct BridgeJSLink { var classLines: [String] = [] var dtsExportLines: [String] = [] var dtsClassLines: [String] = [] + var namespacedClassDtsExportEntries: [String: [String]] = [:] var topLevelTypeLines: [String] = [] var topLevelDtsTypeLines: [String] = [] var importObjectBuilders: [ImportObjectBuilder] = [] @@ -161,13 +199,14 @@ public struct BridgeJSLink { for klass in skeleton.classes { let (jsType, dtsType, dtsExportEntry) = try renderExportedClass(klass) data.classLines.append(contentsOf: jsType) + data.dtsClassLines.append(contentsOf: dtsType) if klass.namespace == nil { data.exportsLines.append("\(klass.name),") data.dtsExportLines.append(contentsOf: dtsExportEntry) + } else { + data.namespacedClassDtsExportEntries[klass.name] = dtsExportEntry } - - data.dtsClassLines.append(contentsOf: dtsType) } // Process enums - collect top-level definitions and export entries @@ -193,7 +232,9 @@ public struct BridgeJSLink { var structExportEntries: [(js: [String], dts: [String])] = [] for structDefinition in skeleton.structs { let (jsStruct, dtsType, dtsExportEntry) = try renderExportedStruct(structDefinition) - data.topLevelDtsTypeLines.append(contentsOf: dtsType) + if structDefinition.namespace == nil { + data.topLevelDtsTypeLines.append(contentsOf: dtsType) + } if structDefinition.namespace == nil && (!jsStruct.isEmpty || !dtsExportEntry.isEmpty) { structExportEntries.append((js: jsStruct, dts: dtsExportEntry)) @@ -306,6 +347,7 @@ public struct BridgeJSLink { "let \(JSGlueVariableScope.reservedF32Stack) = [];", "let \(JSGlueVariableScope.reservedF64Stack) = [];", "let \(JSGlueVariableScope.reservedPointerStack) = [];", + "let \(JSGlueVariableScope.reservedTaStack) = [];", "const \(JSGlueVariableScope.reservedEnumHelpers) = {};", "const \(JSGlueVariableScope.reservedStructHelpers) = {};", "", @@ -448,12 +490,27 @@ public struct BridgeJSLink { printer.write("return \(JSGlueVariableScope.reservedI64Stack).pop();") } printer.write("}") + // Typed array constructors indexed by kind (must match _BridgedNumericArrayKind) + printer.write( + "const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array];" + ) + printer.write("bjs[\"swift_js_push_typed_array\"] = function(kind, ptr, count) {") + printer.indent { + printer.write("const Ctor = taCtors[kind];") + printer.write("const byteLen = count * Ctor.BYTES_PER_ELEMENT;") + // slice() copies the bytes into a new ArrayBuffer that is properly aligned + printer.write( + "const copy = \(JSGlueVariableScope.reservedMemory).buffer.slice(ptr, ptr + byteLen);" + ) + printer.write("\(JSGlueVariableScope.reservedTaStack).push(Array.from(new Ctor(copy)));") + } + printer.write("}") if !allStructs.isEmpty { for structDef in allStructs { printer.write("bjs[\"swift_js_struct_lower_\(structDef.abiName)\"] = function(objectId) {") printer.indent { printer.write( - "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.name).lower(\(JSGlueVariableScope.reservedSwift).memory.getObject(objectId));" + "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName).lower(\(JSGlueVariableScope.reservedSwift).memory.getObject(objectId));" ) } printer.write("}") @@ -461,7 +518,7 @@ public struct BridgeJSLink { printer.write("bjs[\"swift_js_struct_lift_\(structDef.abiName)\"] = function() {") printer.indent { printer.write( - "const value = \(JSGlueVariableScope.reservedStructHelpers).\(structDef.name).lift();" + "const value = \(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName).lift();" ) printer.write("return \(JSGlueVariableScope.reservedSwift).memory.retain(value);") } @@ -636,11 +693,10 @@ public struct BridgeJSLink { for unified in skeletons { let moduleName = unified.moduleName - let collector = ClosureSignatureCollectorVisitor() - var walker = BridgeTypeWalker(visitor: collector) + let collector = ClosureSignatureCollectorVisitor(moduleName: moduleName) + var walker = BridgeSkeletonWalker(visitor: collector) walker.walk(unified) let closureSignatures = walker.visitor.signatures - guard !closureSignatures.isEmpty else { continue } intrinsicRegistry.register(name: "swiftClosureHelpers") { helperPrinter in @@ -742,7 +798,12 @@ public struct BridgeJSLink { signature: ClosureSignature, functionName: String ) throws -> [String] { - let thunkBuilder = ImportedThunkBuilder(context: .exportSwift, intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: signature.isAsync, isThrows: signature.isThrows), + returnType: signature.returnType, + context: .exportSwift, + intrinsicRegistry: intrinsicRegistry + ) thunkBuilder.parameterNames.append("callbackId") thunkBuilder.body.write("const callback = \(JSGlueVariableScope.reservedSwift).memory.getObject(callbackId);") @@ -751,13 +812,9 @@ public struct BridgeJSLink { try thunkBuilder.liftParameter(param: Parameter(label: nil, name: paramName, type: paramType)) } - let returnExpr = try thunkBuilder.call(calleeExpr: "callback", returnType: signature.returnType) + try thunkBuilder.call(calleeExpr: "callback") - var functionLines = thunkBuilder.renderFunction( - name: nil, - returnExpr: returnExpr, - returnType: signature.returnType - ) + var functionLines = thunkBuilder.renderFunction(name: nil) functionLines[0] = "bjs[\"\(functionName)\"] = " + functionLines[0] return functionLines @@ -884,32 +941,21 @@ public struct BridgeJSLink { printer.write(lines: generateImportedTypeDefinitions()) // Exports type + let hierarchicalExportLines = namespaceBuilder.buildHierarchicalExportsType( + exportedSkeletons: exportedSkeletons, + renderClassEntry: { klass in + data.namespacedClassDtsExportEntries[klass.name] ?? [] + }, + renderFunctionSignature: { function in + "\(function.name)\(self.renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" + } + ) printer.write("export type Exports = {") printer.indent { // Add non-namespaced items printer.write(lines: data.dtsExportLines) - // Add hierarchical namespaced items - let hierarchicalLines = namespaceBuilder.buildHierarchicalExportsType( - exportedSkeletons: exportedSkeletons, - renderClassEntry: { klass in - let printer = CodeFragmentPrinter() - printer.write("\(klass.name): {") - printer.indent { - if let constructor = klass.constructor { - printer.write( - "new\(self.renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name), effects: constructor.effects));" - ) - } - } - printer.write("}") - return printer.lines - }, - renderFunctionSignature: { function in - "\(function.name)\(self.renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" - } - ) - printer.write(lines: hierarchicalLines) + printer.write(lines: hierarchicalExportLines) } printer.write("}") @@ -977,7 +1023,7 @@ public struct BridgeJSLink { let structScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) let fragment = IntrinsicJSFragment.structHelper(structDefinition: structDef, allStructs: allStructs) _ = try fragment.printCode( - [structDef.name], + [structDef.abiName], IntrinsicJSFragment.PrintCodeContext( scope: structScope, printer: structPrinter, @@ -1131,10 +1177,10 @@ public struct BridgeJSLink { for skeleton in skeletons.compactMap(\.exported) { for structDef in skeleton.structs { printer.write( - "const \(structDef.name)Helpers = __bjs_create\(structDef.name)Helpers();" + "const \(structDef.abiName)Helpers = __bjs_create\(structDef.abiName)Helpers();" ) printer.write( - "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.name) = \(structDef.name)Helpers;" + "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName) = \(structDef.abiName)Helpers;" ) printer.nextLine() } @@ -1233,7 +1279,7 @@ public struct BridgeJSLink { for method in type.methods { let methodName = method.jsName ?? method.name let methodSignature = - "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: Effects(isAsync: false, isThrows: false)));" + "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" printer.write(methodSignature) } @@ -1413,8 +1459,6 @@ public struct BridgeJSLink { } } return type.tsType - case .swiftStruct(let name): - return name.components(separatedBy: ".").last ?? name case .nullable(let wrapped, let kind): let base = resolveTypeScriptType(wrapped, exportedSkeletons: exportedSkeletons) return "\(base) | \(kind.absenceLiteral)" @@ -1974,13 +2018,24 @@ extension BridgeJSLink { dtsExportEntryPrinter.write("\(klass.name): {") jsPrinter.write("class \(klass.name) extends SwiftHeapObject {") - // Always add __construct and constructor methods for all classes + // Per-class identity mode: determine at codegen time whether this class uses identity caching + let useIdentity = shouldUseIdentityCache(for: klass) jsPrinter.indent { + if useIdentity { + jsPrinter.write("static __identityCache = new Map();") + jsPrinter.nextLine() + } jsPrinter.write("static __construct(ptr) {") jsPrinter.indent { - jsPrinter.write( - "return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_\(klass.abiName)_deinit, \(klass.name).prototype);" - ) + if useIdentity { + jsPrinter.write( + "return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_\(klass.abiName)_deinit, \(klass.name).prototype, \(klass.name).__identityCache);" + ) + } else { + jsPrinter.write( + "return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_\(klass.abiName)_deinit, \(klass.name).prototype, null);" + ) + } } jsPrinter.write("}") jsPrinter.nextLine() @@ -2000,10 +2055,11 @@ extension BridgeJSLink { jsPrinter.indent { jsPrinter.write("constructor(\(constructorParamList)) {") let returnExpr = thunkBuilder.callConstructor(abiName: constructor.abiName) + let constructCall = "\(klass.name).__construct(\(returnExpr))" jsPrinter.indent { thunkBuilder.renderFunctionBody( into: jsPrinter, - returnExpr: "\(klass.name).__construct(\(returnExpr))" + returnExpr: constructCall ) } jsPrinter.write("}") @@ -2164,15 +2220,25 @@ extension BridgeJSLink { class ImportedThunkBuilder { let body: CodeFragmentPrinter let scope: JSGlueVariableScope + let effects: Effects + let returnType: BridgeType let context: BridgeContext var parameterNames: [String] = [] var parameterForwardings: [String] = [] + var returnExpr: String? let printContext: IntrinsicJSFragment.PrintCodeContext - init(context: BridgeContext = .importTS, intrinsicRegistry: JSIntrinsicRegistry) { + init( + effects: Effects, + returnType: BridgeType, + context: BridgeContext = .importTS, + intrinsicRegistry: JSIntrinsicRegistry + ) { self.body = CodeFragmentPrinter() self.scope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) self.context = context + self.effects = effects + self.returnType = returnType self.printContext = IntrinsicJSFragment.PrintCodeContext( scope: scope, printer: body, @@ -2202,7 +2268,15 @@ extension BridgeJSLink { parameterForwardings.append(contentsOf: liftedValues) } - func renderFunction( + func renderFunction(name: String?) -> [String] { + if effects.isAsync { + return renderAsyncFunction(name: name) + } else { + return renderSyncFunction(name: name, returnExpr: returnExpr, returnType: returnType) + } + } + + private func renderSyncFunction( name: String?, returnExpr: String?, returnType: BridgeType @@ -2232,21 +2306,44 @@ extension BridgeJSLink { return printer.lines } - func call(name: String, fromObjectExpr: String, returnType: BridgeType) throws -> String? { + /// Renders an async import function with resolve/reject closure refs. + /// + /// The generated function takes `resolveRef` and `rejectRef` as the first parameters, + /// looks up the resolve/reject closures from memory, and executes the body which + /// chains `.then(resolve, reject)` on the import's returned Promise. + private func renderAsyncFunction(name: String?) -> [String] { + let printer = CodeFragmentPrinter() + let allParams = ["resolveRef", "rejectRef"] + parameterNames + printer.write("function\(name.map { " \($0)" } ?? "")(\(allParams.joined(separator: ", "))) {") + printer.indent { + let s = JSGlueVariableScope.reservedSwift + printer.write("const resolve = \(s).memory.getObject(resolveRef);") + printer.write("const reject = \(s).memory.getObject(rejectRef);") + printer.write(contentsOf: body) + } + printer.write("}") + return printer.lines + } + + func call(name: String, fromObjectExpr: String) throws { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) - return try self.call(calleeExpr: calleeExpr, returnType: returnType) + return try self.call(calleeExpr: calleeExpr) } - func call(name: String, returnType: BridgeType) throws -> String? { - return try call(name: name, fromObjectExpr: "imports", returnType: returnType) + func call(name: String) throws { + return try call(name: name, fromObjectExpr: "imports") } - func call(calleeExpr: String, returnType: BridgeType) throws -> String? { + func call(calleeExpr: String) throws { let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - return try self.call(callExpr: callExpr, returnType: returnType) + try self.call(callExpr: callExpr) } - private func call(callExpr: String, returnType: BridgeType) throws -> String? { + private func call(callExpr: String) throws { + if effects.isAsync { + body.write("\(callExpr).then(resolve, reject);") + return + } let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: returnType, context: context) let returnExpr: String? if loweringFragment.parameters.count == 0 { @@ -2257,43 +2354,49 @@ extension BridgeJSLink { body.write("let \(resultVariable) = \(callExpr);") returnExpr = resultVariable } - return try lowerReturnValue( + self.returnExpr = try lowerReturnValue( returnType: returnType, returnExpr: returnExpr, loweringFragment: loweringFragment ) } - func callConstructor(jsName: String, swiftTypeName: String, fromObjectExpr: String) throws -> String? { + func callConstructor(jsName: String, swiftTypeName: String, fromObjectExpr: String) throws { let ctorExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: jsName) let call = "new \(ctorExpr)(\(parameterForwardings.joined(separator: ", ")))" let type: BridgeType = .jsObject(swiftTypeName) let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: type, context: context) - return try lowerReturnValue(returnType: type, returnExpr: call, loweringFragment: loweringFragment) + self.returnExpr = try lowerReturnValue( + returnType: type, + returnExpr: call, + loweringFragment: loweringFragment + ) } - func callConstructor(jsName: String, swiftTypeName: String) throws -> String? { + func callConstructor(jsName: String, swiftTypeName: String) throws { return try callConstructor(jsName: jsName, swiftTypeName: swiftTypeName, fromObjectExpr: "imports") } - func callMethod(name: String, returnType: BridgeType) throws -> String? { + func callMethod(name: String) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - return try call( - calleeExpr: calleeExpr, - returnType: returnType - ) + return try call(calleeExpr: calleeExpr) } - func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws -> String? { + /// Generates an async method call with resolve/reject closure refs. + func callAsyncMethod(name: String) { + let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - return try call( - calleeExpr: calleeExpr, - returnType: returnType - ) + let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" + body.write("\(callExpr).then(resolve, reject);") } - func callPropertyGetter(name: String, returnType: BridgeType) throws -> String? { + func callStaticMethod(on objectExpr: String, name: String) throws { + let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) + return try call(calleeExpr: calleeExpr) + } + + func callPropertyGetter(name: String) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) if context == .exportSwift, returnType.usesSideChannelForOptionalReturn() { @@ -2308,24 +2411,22 @@ extension BridgeJSLink { let fragment = try IntrinsicJSFragment.protocolPropertyOptionalToSideChannel(wrappedType: wrappedType) _ = try fragment.printCode([resultVar], printContext) - - return nil // Side-channel types return nil (no direct return value) + // Side-channel types return nil (no direct return value) + self.returnExpr = nil + return } - return try call( - callExpr: accessExpr, - returnType: returnType - ) + return try call(callExpr: accessExpr) } - func callPropertySetter(name: String, returnType: BridgeType) { + func callPropertySetter(name: String) { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) let call = "\(accessExpr) = \(parameterForwardings.joined(separator: ", "))" body.write("\(call);") } - func getImportProperty(name: String, fromObjectExpr: String, returnType: BridgeType) throws -> String? { + func getImportProperty(name: String, fromObjectExpr: String, returnType: BridgeType) throws { if returnType == .void { throw BridgeJSLinkError(message: "Void is not supported for imported JS properties") } @@ -2343,14 +2444,14 @@ extension BridgeJSLink { returnExpr = resultVariable } - return try lowerReturnValue( + self.returnExpr = try lowerReturnValue( returnType: returnType, returnExpr: returnExpr, loweringFragment: loweringFragment ) } - func getImportProperty(name: String, returnType: BridgeType) throws -> String? { + func getImportProperty(name: String, returnType: BridgeType) throws { return try getImportProperty(name: name, fromObjectExpr: "imports", returnType: returnType) } @@ -2533,6 +2634,7 @@ extension BridgeJSLink { var functions: [ExportedFunction] = [] var classes: [ExportedClass] = [] var enums: [ExportedEnum] = [] + var structs: [ExportedStruct] = [] var staticProperties: [ExportedProperty] = [] var functionJsLines: [(name: String, lines: [String])] = [] var functionDtsLines: [(name: String, lines: [String])] = [] @@ -2581,6 +2683,14 @@ extension BridgeJSLink { currentNode.content.classes.append(klass) } + for structDef in skeleton.structs where structDef.namespace != nil { + var currentNode = rootNode + for part in structDef.namespace! { + currentNode = currentNode.addChild(part) + } + currentNode.content.structs.append(structDef) + } + for enumDef in skeleton.enums where enumDef.namespace != nil && enumDef.enumType != .namespace { var currentNode = rootNode for part in enumDef.namespace! { @@ -2762,8 +2872,18 @@ extension BridgeJSLink { } } + private func hasExportContent(node: NamespaceNode) -> Bool { + if !node.content.classDtsLines.isEmpty || !node.content.enumDtsLines.isEmpty + || !node.content.functionDtsLines.isEmpty || !node.content.staticProperties.isEmpty + { + return true + } + return node.children.values.contains(where: { hasExportContent(node: $0) }) + } + private func printExportsTypeHierarchy(node: NamespaceNode, printer: CodeFragmentPrinter) { for (childName, childNode) in node.children.sorted(by: { $0.key < $1.key }) { + guard hasExportContent(node: childNode) else { continue } printer.write("\(childName): {") printer.indent { for (_, lines) in childNode.content.classDtsLines.sorted(by: { $0.name < $1.name }) { @@ -2900,8 +3020,8 @@ extension BridgeJSLink { renderTSSignatureCallback: @escaping ([Parameter], BridgeType, Effects) -> String ) { func hasContent(node: NamespaceNode) -> Bool { - // Enums are always included - if !node.content.enums.isEmpty { + // Enums and structs are always included + if !node.content.enums.isEmpty || !node.content.structs.isEmpty { return true } @@ -3081,6 +3201,23 @@ extension BridgeJSLink { } } + // Generate struct interface definitions + let sortedStructs = childNode.content.structs.sorted { $0.name < $1.name } + for structDef in sortedStructs { + let instanceProps = structDef.properties.filter { !$0.isStatic } + printer.write("export interface \(structDef.name) {") + printer.indent { + for property in instanceProps { + let tsType = BridgeJSLink.resolveTypeScriptType( + property.type, + exportedSkeletons: exportedSkeletons + ) + printer.write("\(property.name): \(tsType);") + } + } + printer.write("}") + } + // Only include functions and properties when exposeToGlobal is true if exposeToGlobal { let sortedFunctions = childNode.content.functions.sorted { $0.name < $1.name } @@ -3118,27 +3255,23 @@ extension BridgeJSLink { importObjectBuilder: ImportObjectBuilder, function: ImportedFunctionSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: function.effects, + returnType: function.returnType, + intrinsicRegistry: intrinsicRegistry + ) for param in function.parameters { try thunkBuilder.liftParameter(param: param) } let jsName = function.jsName ?? function.name let importRootExpr = function.from == .global ? "globalThis" : "imports" - let returnExpr = try thunkBuilder.call( - name: jsName, - fromObjectExpr: importRootExpr, - returnType: function.returnType - ) - let funcLines = thunkBuilder.renderFunction( - name: function.abiName(context: nil), - returnExpr: returnExpr, - returnType: function.returnType - ) - let effects = Effects(isAsync: false, isThrows: false) + + try thunkBuilder.call(name: jsName, fromObjectExpr: importRootExpr) + let funcLines = thunkBuilder.renderFunction(name: function.abiName(context: nil)) if function.from == nil { importObjectBuilder.appendDts( [ - "\(renderTSPropertyName(jsName))\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: effects));" + "\(renderTSPropertyName(jsName))\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" ] ) } @@ -3149,20 +3282,20 @@ extension BridgeJSLink { importObjectBuilder: ImportObjectBuilder, getter: ImportedGetterSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: getter.type, + intrinsicRegistry: intrinsicRegistry + ) let jsName = getter.jsName ?? getter.name let importRootExpr = getter.from == .global ? "globalThis" : "imports" - let returnExpr = try thunkBuilder.getImportProperty( + try thunkBuilder.getImportProperty( name: jsName, fromObjectExpr: importRootExpr, returnType: getter.type ) let abiName = getter.abiName(context: nil) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: getter.type - ) + let funcLines = thunkBuilder.renderFunction(name: abiName) if getter.from == nil { importObjectBuilder.appendDts(["readonly \(renderTSPropertyName(jsName)): \(getter.type.tsType);"]) } @@ -3186,10 +3319,7 @@ extension BridgeJSLink { getter: getter, abiName: getterAbiName, emitCall: { thunkBuilder in - return try thunkBuilder.callPropertyGetter( - name: getter.jsName ?? getter.name, - returnType: getter.type - ) + return try thunkBuilder.callPropertyGetter(name: getter.jsName ?? getter.name) } ) importObjectBuilder.assignToImportObject(name: getterAbiName, function: js) @@ -3205,8 +3335,7 @@ extension BridgeJSLink { try thunkBuilder.liftParameter( param: Parameter(label: nil, name: "newValue", type: setter.type) ) - thunkBuilder.callPropertySetter(name: setter.jsName ?? setter.name, returnType: setter.type) - return nil + thunkBuilder.callPropertySetter(name: setter.jsName ?? setter.name) } ) importObjectBuilder.assignToImportObject(name: setterAbiName, function: js) @@ -3231,7 +3360,7 @@ extension BridgeJSLink { for method in type.staticMethods { let methodName = method.jsName ?? method.name let signature = - "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: Effects(isAsync: false, isThrows: false)));" + "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" dtsPrinter.write(signature) } } @@ -3250,55 +3379,54 @@ extension BridgeJSLink { type: ImportedTypeSkeleton, constructor: ImportedConstructorSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: BridgeType.jsObject(type.name), + intrinsicRegistry: intrinsicRegistry + ) for param in constructor.parameters { try thunkBuilder.liftParameter(param: param) } - let returnType = BridgeType.jsObject(type.name) let importRootExpr = type.from == .global ? "globalThis" : "imports" - let returnExpr = try thunkBuilder.callConstructor( + try thunkBuilder.callConstructor( jsName: type.jsName ?? type.name, swiftTypeName: type.name, fromObjectExpr: importRootExpr ) let abiName = constructor.abiName(context: type) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: returnType - ) + let funcLines = thunkBuilder.renderFunction(name: abiName) importObjectBuilder.assignToImportObject(name: abiName, function: funcLines) } func renderImportedGetter( getter: ImportedGetterSkeleton, abiName: String, - emitCall: (ImportedThunkBuilder) throws -> String? + emitCall: (ImportedThunkBuilder) throws -> Void ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) - thunkBuilder.liftSelf() - let returnExpr = try emitCall(thunkBuilder) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: getter.type + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: getter.type, + intrinsicRegistry: intrinsicRegistry ) + thunkBuilder.liftSelf() + try emitCall(thunkBuilder) + let funcLines = thunkBuilder.renderFunction(name: abiName) return (funcLines, []) } func renderImportedSetter( setter: ImportedSetterSkeleton, abiName: String, - emitCall: (ImportedThunkBuilder) throws -> String? + emitCall: (ImportedThunkBuilder) throws -> Void ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) - thunkBuilder.liftSelf() - let returnExpr = try emitCall(thunkBuilder) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: .void + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: .void, + intrinsicRegistry: intrinsicRegistry ) + thunkBuilder.liftSelf() + try emitCall(thunkBuilder) + let funcLines = thunkBuilder.renderFunction(name: abiName) return (funcLines, []) } @@ -3306,7 +3434,11 @@ extension BridgeJSLink { context: ImportedTypeSkeleton, method: ImportedFunctionSkeleton ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: method.effects, + returnType: method.returnType, + intrinsicRegistry: intrinsicRegistry + ) for param in method.parameters { try thunkBuilder.liftParameter(param: param) } @@ -3315,16 +3447,9 @@ extension BridgeJSLink { objectExpr: importRootExpr, propertyName: context.jsName ?? context.name ) - let returnExpr = try thunkBuilder.callStaticMethod( - on: constructorExpr, - name: method.jsName ?? method.name, - returnType: method.returnType - ) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName(context: context, operation: "static"), - returnExpr: returnExpr, - returnType: method.returnType - ) + + try thunkBuilder.callStaticMethod(on: constructorExpr, name: method.jsName ?? method.name) + let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context, operation: "static")) return (funcLines, []) } @@ -3332,17 +3457,18 @@ extension BridgeJSLink { context: ImportedTypeSkeleton, method: ImportedFunctionSkeleton ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: method.effects, + returnType: method.returnType, + intrinsicRegistry: intrinsicRegistry + ) thunkBuilder.liftSelf() for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: method.returnType) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName(context: context), - returnExpr: returnExpr, - returnType: method.returnType - ) + + try thunkBuilder.callMethod(name: method.jsName ?? method.name) + let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context)) return (funcLines, []) } @@ -3360,16 +3486,14 @@ extension BridgeJSLink { ) let getterThunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: property.type, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) getterThunkBuilder.liftSelf() - let returnExpr = try getterThunkBuilder.callPropertyGetter(name: property.name, returnType: property.type) - let getterLines = getterThunkBuilder.renderFunction( - name: getterAbiName, - returnExpr: returnExpr, - returnType: property.type - ) + try getterThunkBuilder.callPropertyGetter(name: property.name) + let getterLines = getterThunkBuilder.renderFunction(name: getterAbiName) importObjectBuilder.assignToImportObject(name: getterAbiName, function: getterLines) if !property.isReadonly { @@ -3381,6 +3505,8 @@ extension BridgeJSLink { className: `protocol`.name ) let setterThunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false), + returnType: .void, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) @@ -3388,12 +3514,8 @@ extension BridgeJSLink { try setterThunkBuilder.liftParameter( param: Parameter(label: nil, name: "value", type: property.type) ) - setterThunkBuilder.callPropertySetter(name: property.name, returnType: property.type) - let setterLines = setterThunkBuilder.renderFunction( - name: setterAbiName, - returnExpr: nil, - returnType: .void - ) + setterThunkBuilder.callPropertySetter(name: property.name) + let setterLines = setterThunkBuilder.renderFunction(name: setterAbiName) importObjectBuilder.assignToImportObject(name: setterAbiName, function: setterLines) } } @@ -3404,6 +3526,8 @@ extension BridgeJSLink { method: ExportedFunction ) throws { let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false), + returnType: method.returnType, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) @@ -3411,12 +3535,8 @@ extension BridgeJSLink { for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let returnExpr = try thunkBuilder.callMethod(name: method.name, returnType: method.returnType) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName, - returnExpr: returnExpr, - returnType: method.returnType - ) + try thunkBuilder.callMethod(name: method.name) + let funcLines = thunkBuilder.renderFunction(name: method.abiName) importObjectBuilder.assignToImportObject(name: method.abiName, function: funcLines) } } @@ -3528,6 +3648,9 @@ extension BridgeType { case .bool: return "boolean" case .jsObject(let name): + if let name, let tsName = Self.jsTypedArrayTSNames[name] { + return tsName + } return name ?? "any" case .jsValue: return "any" @@ -3544,7 +3667,7 @@ extension BridgeType { case .associatedValueEnum(let name): return "\(name)Tag" case .swiftStruct(let name): - return "\(name)Tag" + return name case .namespaceEnum(let name): return name case .swiftProtocol(let name): @@ -3564,12 +3687,27 @@ extension BridgeType { return "Record" } } + + /// Maps JSTypedArray Swift typealias names to their JavaScript TypedArray constructor names. + private static let jsTypedArrayTSNames: [String: String] = [ + "JSInt8Array": "Int8Array", + "JSUint8Array": "Uint8Array", + "JSInt16Array": "Int16Array", + "JSUint16Array": "Uint16Array", + "JSInt32Array": "Int32Array", + "JSUint32Array": "Uint32Array", + "JSFloat32Array": "Float32Array", + "JSFloat64Array": "Float64Array", + ] } extension WasmCoreType { fileprivate var placeholderValue: String { switch self { - case .i32, .i64, .f32, .f64, .pointer: return "0" + // A Wasm `i64` return is a JavaScript `BigInt`, so the error-path placeholder + // must be a BigInt literal rather than a plain number. + case .i64: return "0n" + case .i32, .f32, .f64, .pointer: return "0" } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 599b6df39..388d703bd 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -34,6 +34,7 @@ final class JSGlueVariableScope { static let reservedStructHelpers = "structHelpers" static let reservedSwiftClosureRegistry = "swiftClosureRegistry" static let reservedMakeSwiftClosure = "makeClosure" + static let reservedTaStack = "taStack" private let intrinsicRegistry: JSIntrinsicRegistry @@ -63,6 +64,7 @@ final class JSGlueVariableScope { reservedStructHelpers, reservedSwiftClosureRegistry, reservedMakeSwiftClosure, + reservedTaStack, ] init(intrinsicRegistry: JSIntrinsicRegistry) { @@ -944,7 +946,7 @@ struct IntrinsicJSFragment: Sendable { fullName: String, kind: JSOptionalKind ) -> IntrinsicJSFragment { - let base = fullName.components(separatedBy: ".").last ?? fullName + let base = fullName.replacingOccurrences(of: ".", with: "_") let absenceLiteral = kind.absenceLiteral return IntrinsicJSFragment( parameters: [], @@ -1300,7 +1302,7 @@ struct IntrinsicJSFragment: Sendable { let base = fullName.components(separatedBy: ".").last ?? fullName return .associatedEnumLowerParameter(enumBase: base) case .swiftStruct(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName + let base = fullName.replacingOccurrences(of: ".", with: "_") return swiftStructLowerParameter(structBase: base) case .closure: return IntrinsicJSFragment( @@ -1360,7 +1362,7 @@ struct IntrinsicJSFragment: Sendable { let base = fullName.components(separatedBy: ".").last ?? fullName return .associatedEnumLiftReturn(enumBase: base) case .swiftStruct(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName + let base = fullName.replacingOccurrences(of: ".", with: "_") return swiftStructLiftReturn(structBase: base) case .closure: return IntrinsicJSFragment( @@ -1446,7 +1448,7 @@ struct IntrinsicJSFragment: Sendable { case .importTS: return .jsObjectLiftRetainedObjectId case .exportSwift: - let base = fullName.components(separatedBy: ".").last ?? fullName + let base = fullName.replacingOccurrences(of: ".", with: "_") return IntrinsicJSFragment( parameters: [], printCode: { arguments, context in @@ -1805,7 +1807,7 @@ struct IntrinsicJSFragment: Sendable { } static func swiftStructLowerReturn(fullName: String) -> IntrinsicJSFragment { - swiftStructLower(structBase: fullName.components(separatedBy: ".").last ?? fullName) + swiftStructLower(structBase: fullName.replacingOccurrences(of: ".", with: "_")) } static func swiftStructLowerParameter(structBase: String) -> IntrinsicJSFragment { @@ -1896,20 +1898,31 @@ struct IntrinsicJSFragment: Sendable { let (scope, printer) = (context.scope, context.printer) let resultVar = scope.variable("arrayResult") let lenVar = scope.variable("arrayLen") - let iVar = scope.variable("i") printer.write("const \(lenVar) = \(scope.popI32());") - printer.write("const \(resultVar) = [];") - printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {") + printer.write("let \(resultVar);") + printer.write("if (\(lenVar) === -1) {") + printer.indent { + // Bulk path: Swift pushed a typed array onto the typed-array stack + printer.write("\(resultVar) = \(JSGlueVariableScope.reservedTaStack).pop();") + } + printer.write("} else {") try printer.indent { - let elementFragment = try stackLiftFragment(elementType: elementType) - let elementResults = try elementFragment.printCode([], context) - if let elementExpr = elementResults.first { - printer.write("\(resultVar).push(\(elementExpr));") + // Element-by-element path (original behavior) + let iVar = scope.variable("i") + printer.write("\(resultVar) = [];") + printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {") + try printer.indent { + let elementFragment = try stackLiftFragment(elementType: elementType) + let elementResults = try elementFragment.printCode([], context) + if let elementExpr = elementResults.first { + printer.write("\(resultVar).push(\(elementExpr));") + } } + printer.write("}") + printer.write("\(resultVar).reverse();") } printer.write("}") - printer.write("\(resultVar).reverse();") return [resultVar] } ) @@ -2008,7 +2021,7 @@ struct IntrinsicJSFragment: Sendable { } ) case .swiftStruct(let fullName): - let structBase = fullName.components(separatedBy: ".").last ?? fullName + let structBase = fullName.replacingOccurrences(of: ".", with: "_") return IntrinsicJSFragment( parameters: [], printCode: { arguments, context in @@ -2130,7 +2143,7 @@ struct IntrinsicJSFragment: Sendable { } ) case .swiftStruct(let fullName): - let structBase = fullName.components(separatedBy: ".").last ?? fullName + let structBase = fullName.replacingOccurrences(of: ".", with: "_") return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in @@ -2426,7 +2439,7 @@ struct IntrinsicJSFragment: Sendable { ) try printer.indent { printer.write( - "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.name).lower(this);" + "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName).lower(this);" ) var paramForwardings: [String] = [] @@ -2502,7 +2515,7 @@ struct IntrinsicJSFragment: Sendable { let printer = context.printer let value = arguments[0] printer.write( - "\(JSGlueVariableScope.reservedStructHelpers).\(nestedName).lower(\(value));" + "\(JSGlueVariableScope.reservedStructHelpers).\(nestedName.replacingOccurrences(of: ".", with: "_")).lower(\(value));" ) return [] } @@ -2540,7 +2553,7 @@ struct IntrinsicJSFragment: Sendable { let (scope, printer) = (context.scope, context.printer) let structVar = scope.variable("struct") printer.write( - "const \(structVar) = \(JSGlueVariableScope.reservedStructHelpers).\(nestedName).lift();" + "const \(structVar) = \(JSGlueVariableScope.reservedStructHelpers).\(nestedName.replacingOccurrences(of: ".", with: "_")).lift();" ) return [structVar] } @@ -2590,7 +2603,10 @@ fileprivate extension WasmCoreType { var jsZeroLiteral: String { switch self { case .f32, .f64: return "0.0" - case .i32, .i64, .pointer: return "0" + // A Wasm `i64` parameter is passed as a JavaScript `BigInt`, so its zero + // placeholder must be a BigInt literal rather than a plain number. + case .i64: return "0n" + case .i32, .pointer: return "0" } } } @@ -2648,7 +2664,7 @@ private extension BridgeType { case .string: return .sideChannelReturn(.storage) case .jsObject: - return .sideChannelReturn(.retainedObject) + return .stackABI case .jsValue: return .inlineFlag case .swiftHeapObject: @@ -2685,7 +2701,7 @@ private extension BridgeType { var nilSentinel: NilSentinel { switch self { - case .jsObject, .swiftProtocol: + case .swiftProtocol: return .i32(0) case .swiftHeapObject: return .pointer diff --git a/Plugins/BridgeJS/Sources/BridgeJSPluginUtilities/PluginPaths.swift b/Plugins/BridgeJS/Sources/BridgeJSPluginUtilities/PluginPaths.swift new file mode 100644 index 000000000..f95d1adaa --- /dev/null +++ b/Plugins/BridgeJS/Sources/BridgeJSPluginUtilities/PluginPaths.swift @@ -0,0 +1,74 @@ +#if canImport(PackagePlugin) +import struct Foundation.URL + +enum BridgeJSPluginPaths { + static func skeletonURL( + targetName: String, + packageID: String, + buildPluginWorkDirectoryURL workDirectoryURL: URL + ) -> URL { + let pluginsOutputsRootURL = + workDirectoryURL + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + return bridgeJSDirectoryURL( + targetName: targetName, + packageID: packageID, + pluginsOutputsRootURL: pluginsOutputsRootURL + ) + .appending(path: "JavaScript/BridgeJS.json") + } + + static func skeletonURL( + targetName: String, + packageID: String, + commandPluginWorkDirectoryURL workDirectoryURL: URL + ) -> URL { + // workDirectoryURL: ".build/plugins/PackageToJS/outputs/" + // .build/plugins/outputs/[package]/[target]/destination/BridgeJS/JavaScript/BridgeJS.json + let pluginsOutputsRootURL = + workDirectoryURL + .deletingLastPathComponent() + .deletingLastPathComponent() + .appending(path: "outputs") + return bridgeJSDirectoryURL( + targetName: targetName, + packageID: packageID, + pluginsOutputsRootURL: pluginsOutputsRootURL + ) + .appending(path: "JavaScript/BridgeJS.json") + } + + static func bridgeJSSwiftURL( + targetName: String, + packageID: String, + buildPluginWorkDirectoryURL workDirectoryURL: URL + ) -> URL { + let pluginsOutputsRootURL = + workDirectoryURL + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + return bridgeJSDirectoryURL( + targetName: targetName, + packageID: packageID, + pluginsOutputsRootURL: pluginsOutputsRootURL + ) + .appending(path: "BridgeJS.swift") + } + + private static func bridgeJSDirectoryURL( + targetName: String, + packageID: String, + pluginsOutputsRootURL: URL + ) -> URL { + pluginsOutputsRootURL + .appending(path: packageID) + .appending(path: targetName) + .appending(path: "destination/BridgeJS") + } +} +#endif diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 1f03e09ba..346b7333b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -89,6 +89,38 @@ public enum BridgeContext: Sendable { case exportSwift } +/// Access level applied to bridge-generated declarations. +/// +/// Ordering (`Comparable`) reflects visibility breadth, so taking `max` of two +/// levels yields the more permissive one — used to merge a single closure +/// signature seen across surfaces with different declared access. +public enum BridgeJSAccessLevel: String, Codable, Equatable, Hashable, Sendable, Comparable { + case `internal` + case package + case `public` + + public static func < (lhs: BridgeJSAccessLevel, rhs: BridgeJSAccessLevel) -> Bool { + lhs.order < rhs.order + } + + private var order: Int { + switch self { + case .internal: return 0 + case .package: return 1 + case .public: return 2 + } + } + + /// Returns the modifier keyword to emit, or nil for the default (internal). + public var modifierKeyword: String? { + switch self { + case .internal: return nil + case .package: return "package" + case .public: return "public" + } + } +} + public struct ClosureSignature: Codable, Equatable, Hashable, Sendable { public let parameters: [BridgeType] public let returnType: BridgeType @@ -101,24 +133,31 @@ public struct ClosureSignature: Codable, Equatable, Hashable, Sendable { public let isAsync: Bool public let isThrows: Bool public let moduleName: String + /// When true, closure parameters are annotated with `sending` in Swift. + /// Used for async Promise resolve/reject callbacks where values are + /// transferred through a continuation. + public let sendingParameters: Bool public init( parameters: [BridgeType], returnType: BridgeType, moduleName: String, isAsync: Bool = false, - isThrows: Bool = false + isThrows: Bool = false, + sendingParameters: Bool = false ) { self.parameters = parameters self.returnType = returnType self.moduleName = moduleName self.isAsync = isAsync self.isThrows = isThrows + self.sendingParameters = sendingParameters let paramPart = parameters.isEmpty ? "y" : parameters.map { $0.mangleTypeName }.joined() - let signaturePart = "\(paramPart)_\(returnType.mangleTypeName)" + let sendingPart = sendingParameters ? "s" : "" + let signaturePart = "\(sendingPart)\(paramPart)_\(returnType.mangleTypeName)" self.mangleName = "\(moduleName.count)\(moduleName)\(signaturePart)" } } @@ -388,14 +427,37 @@ public struct Parameter: Codable, Equatable, Sendable { } } -// MARK: - BridgeType Visitor +// MARK: - BridgeSkeleton Visitor + +public protocol BridgeSkeletonVisitor { + /// Called when a closure type is encountered during traversal. + /// + /// `accessLevel` reflects the source access of the enclosing declaration, + /// so visitors can derive an appropriate access level for any + /// bridge-generated helpers tied to this signature (e.g. typed closure + /// inits in `ClosureCodegen`). + mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) + mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) +} -public protocol BridgeTypeVisitor { - mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) +public extension BridgeSkeletonVisitor { + mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) {} + mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) {} } -public struct BridgeTypeWalker { +public struct BridgeSkeletonWalker { public var visitor: Visitor + /// Tracks the access level of the enclosing declaration during traversal. + /// Saved/restored around each declaration that introduces an access boundary. + private var currentAccessLevel: BridgeJSAccessLevel = .internal public init(visitor: Visitor) { self.visitor = visitor @@ -404,7 +466,11 @@ public struct BridgeTypeWalker { public mutating func walk(_ type: BridgeType) { switch type { case .closure(let signature, let useJSTypedClosure): - visitor.visitClosure(signature, useJSTypedClosure: useJSTypedClosure) + visitor.visitClosure( + signature, + useJSTypedClosure: useJSTypedClosure, + accessLevel: currentAccessLevel + ) for paramType in signature.parameters { walk(paramType) } @@ -436,14 +502,16 @@ public struct BridgeTypeWalker { walk(function) } for klass in skeleton.classes { - if let constructor = klass.constructor { - walk(constructor.parameters) - } - for method in klass.methods { - walk(method) - } - for property in klass.properties { - walk(property.type) + withAccessLevel(klass.explicitAccessControl) { + if let constructor = klass.constructor { + $0.walk(constructor.parameters) + } + for method in klass.methods { + $0.walk(method) + } + for property in klass.properties { + $0.walk(property.type) + } } } for proto in skeleton.protocols { @@ -455,57 +523,66 @@ public struct BridgeTypeWalker { } } for structDecl in skeleton.structs { - for property in structDecl.properties { - walk(property.type) - } - if let constructor = structDecl.constructor { - walk(constructor.parameters) - } - for method in structDecl.methods { - walk(method) + withAccessLevel(structDecl.explicitAccessControl) { + for property in structDecl.properties { + $0.walk(property.type) + } + if let constructor = structDecl.constructor { + $0.walk(constructor.parameters) + } + for method in structDecl.methods { + $0.walk(method) + } } } for enumDecl in skeleton.enums { - for enumCase in enumDecl.cases { - for associatedValue in enumCase.associatedValues { - walk(associatedValue.type) + withAccessLevel(enumDecl.explicitAccessControl) { + for enumCase in enumDecl.cases { + for associatedValue in enumCase.associatedValues { + $0.walk(associatedValue.type) + } + } + for method in enumDecl.staticMethods { + $0.walk(method) + } + for property in enumDecl.staticProperties { + $0.walk(property.type) } - } - for method in enumDecl.staticMethods { - walk(method) - } - for property in enumDecl.staticProperties { - walk(property.type) } } } public mutating func walk(_ function: ImportedFunctionSkeleton) { - walk(function.parameters) - walk(function.returnType) + visitor.visitImportedFunction(function) + withAccessLevel(function.accessLevel) { + $0.walk(function.parameters) + $0.walk(function.returnType) + } } public mutating func walk(_ skeleton: ImportedModuleSkeleton) { for fileSkeleton in skeleton.children { for getter in fileSkeleton.globalGetters { - walk(getter.type) + withAccessLevel(getter.accessLevel) { $0.walk(getter.type) } } for setter in fileSkeleton.globalSetters { - walk(setter.type) + withAccessLevel(setter.accessLevel) { $0.walk(setter.type) } } for function in fileSkeleton.functions { walk(function) } for type in fileSkeleton.types { - if let constructor = type.constructor { - walk(constructor.parameters) - } - for getter in type.getters { - walk(getter.type) - } - for setter in type.setters { - walk(setter.type) - } - for method in type.methods + type.staticMethods { - walk(method) + withAccessLevel(type.accessLevel) { + if let constructor = type.constructor { + $0.withAccessLevel(constructor.accessLevel) { $0.walk(constructor.parameters) } + } + for getter in type.getters { + $0.withAccessLevel(getter.accessLevel) { $0.walk(getter.type) } + } + for setter in type.setters { + $0.withAccessLevel(setter.accessLevel) { $0.walk(setter.type) } + } + for method in type.methods + type.staticMethods { + $0.walk(method) + } } } } @@ -518,6 +595,40 @@ public struct BridgeTypeWalker { walk(imported) } } + + /// Sets `currentAccessLevel` to `level` for the duration of `body`, restoring + /// the prior value afterward. A nil level (e.g. for exported decls without + /// an explicit modifier) inherits the outer level rather than overwriting it. + private mutating func withAccessLevel( + _ level: BridgeJSAccessLevel?, + _ body: (inout BridgeSkeletonWalker) -> Void + ) { + let saved = currentAccessLevel + if let level { + currentAccessLevel = level + } + body(&self) + currentAccessLevel = saved + } + + /// String-typed convenience: maps `"public"`/`"package"`/`"internal"` from + /// `Exported*.explicitAccessControl` to the typed enum. Unknown strings + /// (e.g. `"open"`, `"private"`) hit the assert in debug builds and inherit + /// the outer level in release — the `@JSExport` macros reject those cases + /// upstream, so this is a defensive guard against future schema drift. + private mutating func withAccessLevel( + _ rawLevel: String?, + _ body: (inout BridgeSkeletonWalker) -> Void + ) { + let level: BridgeJSAccessLevel? + if let rawLevel { + level = BridgeJSAccessLevel(rawValue: rawLevel) + assert(level != nil, "Unexpected access level string: \(rawLevel)") + } else { + level = nil + } + withAccessLevel(level, body) + } } public struct Effects: Codable, Equatable, Sendable { @@ -770,6 +881,7 @@ public struct ExportedClass: Codable, NamespacedExportedType { public var methods: [ExportedFunction] public var properties: [ExportedProperty] public var namespace: [String]? + public var identityMode: Bool? // nil = use config default, true/false = override public init( name: String, @@ -778,7 +890,8 @@ public struct ExportedClass: Codable, NamespacedExportedType { constructor: ExportedConstructor? = nil, methods: [ExportedFunction], properties: [ExportedProperty] = [], - namespace: [String]? = nil + namespace: [String]? = nil, + identityMode: Bool? = nil ) { self.name = name self.swiftCallName = swiftCallName @@ -787,6 +900,7 @@ public struct ExportedClass: Codable, NamespacedExportedType { self.methods = methods self.properties = properties self.namespace = namespace + self.identityMode = identityMode } } @@ -876,13 +990,20 @@ public struct ExportedSkeleton: Codable { /// through the exports object. public var exposeToGlobal: Bool + /// The identity mode for exported Swift heap objects. + /// + /// When `"pointer"`, Swift heap objects are tracked by pointer identity. + /// When `"none"` or `nil`, no identity tracking is performed. + public var identityMode: String? + public init( functions: [ExportedFunction], classes: [ExportedClass], enums: [ExportedEnum], structs: [ExportedStruct] = [], protocols: [ExportedProtocol] = [], - exposeToGlobal: Bool + exposeToGlobal: Bool, + identityMode: String? = nil ) { self.functions = functions self.classes = classes @@ -890,6 +1011,7 @@ public struct ExportedSkeleton: Codable { self.structs = structs self.protocols = protocols self.exposeToGlobal = exposeToGlobal + self.identityMode = identityMode } public mutating func append(_ other: ExportedSkeleton) { @@ -899,6 +1021,7 @@ public struct ExportedSkeleton: Codable { self.structs.append(contentsOf: other.structs) self.protocols.append(contentsOf: other.protocols) assert(self.exposeToGlobal == other.exposeToGlobal) + assert(self.identityMode == other.identityMode) } public var isEmpty: Bool { @@ -923,7 +1046,12 @@ public struct ImportedFunctionSkeleton: Codable { public let from: JSImportFrom? public let parameters: [Parameter] public let returnType: BridgeType + public let effects: Effects public let documentation: String? + /// Source access level of the originating Swift declaration. Used to + /// determine the access level of bridge-generated helpers (e.g. typed + /// closure inits) that surface through this function's signature. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -931,14 +1059,34 @@ public struct ImportedFunctionSkeleton: Codable { from: JSImportFrom? = nil, parameters: [Parameter], returnType: BridgeType, - documentation: String? = nil + effects: Effects = Effects(isAsync: false, isThrows: true), + documentation: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName self.from = from self.parameters = parameters self.returnType = returnType + self.effects = effects self.documentation = documentation + self.accessLevel = accessLevel + } + + private enum CodingKeys: String, CodingKey { + case name, jsName, from, parameters, returnType, effects, documentation, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.parameters = try container.decode([Parameter].self, forKey: .parameters) + self.returnType = try container.decode(BridgeType.self, forKey: .returnType) + self.effects = try container.decode(Effects.self, forKey: .effects) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -956,9 +1104,23 @@ public struct ImportedFunctionSkeleton: Codable { public struct ImportedConstructorSkeleton: Codable { public let parameters: [Parameter] + /// Source access level of the originating Swift `init`. Inherits from the + /// enclosing `@JSClass` type when not annotated explicitly. + public let accessLevel: BridgeJSAccessLevel - public init(parameters: [Parameter]) { + public init(parameters: [Parameter], accessLevel: BridgeJSAccessLevel = .internal) { self.parameters = parameters + self.accessLevel = accessLevel + } + + private enum CodingKeys: String, CodingKey { + case parameters, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.parameters = try container.decode([Parameter].self, forKey: .parameters) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal } public func abiName(context: ImportedTypeSkeleton) -> String { @@ -979,6 +1141,8 @@ public struct ImportedGetterSkeleton: Codable { public let documentation: String? /// Name of the getter function if it's a separate function (from @JSGetter) public let functionName: String? + /// Source access level of the originating Swift declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -986,7 +1150,8 @@ public struct ImportedGetterSkeleton: Codable { from: JSImportFrom? = nil, type: BridgeType, documentation: String? = nil, - functionName: String? = nil + functionName: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName @@ -994,6 +1159,22 @@ public struct ImportedGetterSkeleton: Codable { self.type = type self.documentation = documentation self.functionName = functionName + self.accessLevel = accessLevel + } + + private enum CodingKeys: String, CodingKey { + case name, jsName, from, type, documentation, functionName, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.type = try container.decode(BridgeType.self, forKey: .type) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.functionName = try container.decodeIfPresent(String.self, forKey: .functionName) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -1020,19 +1201,37 @@ public struct ImportedSetterSkeleton: Codable { public let documentation: String? /// Name of the setter function if it's a separate function (from @JSSetter) public let functionName: String? + /// Source access level of the originating Swift declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, jsName: String? = nil, type: BridgeType, documentation: String? = nil, - functionName: String? = nil + functionName: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName self.type = type self.documentation = documentation self.functionName = functionName + self.accessLevel = accessLevel + } + + private enum CodingKeys: String, CodingKey { + case name, jsName, type, documentation, functionName, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.type = try container.decode(BridgeType.self, forKey: .type) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.functionName = try container.decodeIfPresent(String.self, forKey: .functionName) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -1064,6 +1263,8 @@ public struct ImportedTypeSkeleton: Codable { public let getters: [ImportedGetterSkeleton] public let setters: [ImportedSetterSkeleton] public let documentation: String? + /// Source access level of the originating Swift `@JSClass` declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -1074,7 +1275,8 @@ public struct ImportedTypeSkeleton: Codable { staticMethods: [ImportedFunctionSkeleton] = [], getters: [ImportedGetterSkeleton] = [], setters: [ImportedSetterSkeleton] = [], - documentation: String? = nil + documentation: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName @@ -1085,6 +1287,25 @@ public struct ImportedTypeSkeleton: Codable { self.getters = getters self.setters = setters self.documentation = documentation + self.accessLevel = accessLevel + } + + private enum CodingKeys: String, CodingKey { + case name, jsName, from, constructor, methods, staticMethods, getters, setters, documentation, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.constructor = try container.decodeIfPresent(ImportedConstructorSkeleton.self, forKey: .constructor) + self.methods = try container.decode([ImportedFunctionSkeleton].self, forKey: .methods) + self.staticMethods = try container.decode([ImportedFunctionSkeleton].self, forKey: .staticMethods) + self.getters = try container.decode([ImportedGetterSkeleton].self, forKey: .getters) + self.setters = try container.decode([ImportedSetterSkeleton].self, forKey: .setters) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal } } @@ -1150,15 +1371,104 @@ public struct ImportedModuleSkeleton: Codable { // MARK: - Closure signature collection visitor -public struct ClosureSignatureCollectorVisitor: BridgeTypeVisitor { - public var signatures: Set = [] +public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { + /// Each unique closure signature mapped to the most-permissive access level + /// observed across all surfaces that reference it. The codegen reads this + /// to choose the access modifier for the synthesized typed-closure init. + public private(set) var signatureAccessLevels: [ClosureSignature: BridgeJSAccessLevel] = [:] + /// Convenience view for callers (e.g. `BridgeJSLink`) that only need the + /// set of unique signatures, without access metadata. + public var signatures: Set { Set(signatureAccessLevels.keys) } + let moduleName: String + + /// Convenience for callers that only need to seed signatures without + /// access metadata (e.g. exported-side walking, where closure init access + /// is irrelevant because the synthesized init isn't surfaced to consumers). + /// All seeded signatures default to `.internal`; if a seeded signature is + /// later observed with a more permissive access level, the merge in + /// `recordSignature` upgrades it. + public init(moduleName: String, signatures: Set = []) { + self.moduleName = moduleName + for signature in signatures { + signatureAccessLevels[signature] = .internal + } + } - public init(signatures: Set = []) { - self.signatures = signatures + public mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) { + recordSignature(signature, accessLevel: accessLevel) } - public mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) { - signatures.insert(signature) + /// Insert `signature` at `accessLevel`, or upgrade the existing level to + /// the more permissive of the two. Centralizing the merge here keeps + /// `visitClosure` and `recordInjectedSignature` in lockstep — if the + /// merge policy ever needs to change (e.g. adding a diagnostic for + /// conflicting levels), there's only one place to update. + private mutating func recordSignature( + _ signature: ClosureSignature, + accessLevel: BridgeJSAccessLevel + ) { + if let existing = signatureAccessLevels[signature] { + signatureAccessLevels[signature] = max(existing, accessLevel) + } else { + signatureAccessLevels[signature] = accessLevel + } + } + public mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) { + guard function.effects.isAsync else { return } + + // When async imports exist, inject closure signatures for the typed resolve + // and reject callbacks used by _bjs_awaitPromise. + // - Reject always uses (sending JSValue) -> Void + // - Resolve uses a typed closure matching the return type (or () -> Void for void) + // All async callback closures use `sending` parameters so values can be + // transferred through the checked continuation without Sendable constraints. + + // Reject callback + recordInjectedSignature( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: moduleName, + sendingParameters: true + ), + for: function + ) + // Resolve callback (typed per return type) + if function.returnType == .void { + recordInjectedSignature( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: moduleName + ), + for: function + ) + } else { + recordInjectedSignature( + ClosureSignature( + parameters: [function.returnType], + returnType: .void, + moduleName: moduleName, + sendingParameters: true + ), + for: function + ) + } + } + + /// Inject a closure signature derived from an async import (e.g. Promise + /// resolve/reject callbacks). The injected signature inherits the access + /// level of the originating function so its synthesized init matches the + /// visibility of the async API surface. + private mutating func recordInjectedSignature( + _ signature: ClosureSignature, + for function: ImportedFunctionSkeleton + ) { + recordSignature(signature, accessLevel: function.accessLevel) } } @@ -1169,11 +1479,18 @@ public struct BridgeJSSkeleton: Codable { public let moduleName: String public let exported: ExportedSkeleton? public let imported: ImportedModuleSkeleton? + public let usedExternalModules: [String] - public init(moduleName: String, exported: ExportedSkeleton? = nil, imported: ImportedModuleSkeleton? = nil) { + public init( + moduleName: String, + exported: ExportedSkeleton? = nil, + imported: ImportedModuleSkeleton? = nil, + usedExternalModules: [String] = [] + ) { self.moduleName = moduleName self.exported = exported self.imported = imported + self.usedExternalModules = usedExternalModules } } @@ -1338,7 +1655,7 @@ extension BridgeType { } switch wrappedType { - case .string, .integer, .float, .double, .jsObject, .swiftProtocol: + case .string, .integer, .float, .double, .swiftProtocol: return true case .rawValueEnum(_, let rawType): switch rawType { @@ -1349,7 +1666,7 @@ extension BridgeType { default: return false } - case .bool, .caseEnum, .swiftHeapObject, .associatedValueEnum: + case .bool, .caseEnum, .swiftHeapObject, .associatedValueEnum, .jsObject: return false default: return false diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift index a71aaee44..005af04a8 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift @@ -112,10 +112,18 @@ import BridgeJSUtilities help: "The path to the TypeScript project configuration file (required for .d.ts files)", required: false ), + "dependency-skeleton": OptionRule( + help: "Path to a dependency module's BridgeJS.json, as '='. Repeatable.", + required: false, + repeatable: true + ), ] ) - let (positionalArguments, _, doubleDashOptions) = try parser.parse( - arguments: Array(arguments.dropFirst()) + let parsedArguments = try parser.parse(arguments: Array(arguments.dropFirst())) + let positionalArguments = parsedArguments.positionalArguments + let doubleDashOptions = parsedArguments.doubleDashOptions + let dependencySkeletons = try loadDependencySkeletons( + parsedArguments.repeatedDoubleDashOptions["dependency-skeleton", default: []] ) let progress = ProgressReporting(verbose: doubleDashOptions["verbose"] == "true") let moduleName = doubleDashOptions["module-name"]! @@ -162,10 +170,13 @@ import BridgeJSUtilities inputFiles.append(macrosPath) } } + let externalModuleIndex = ExternalModuleIndex(dependencies: dependencySkeletons) let swiftToSkeleton = SwiftToSkeleton( progress: progress, moduleName: moduleName, - exposeToGlobal: config.exposeToGlobal + exposeToGlobal: config.exposeToGlobal, + externalModuleIndex: externalModuleIndex, + identityMode: config.identityMode ) for inputFile in inputFiles.sorted() { try withSpan("Parsing \(inputFile)") { @@ -223,7 +234,10 @@ import BridgeJSUtilities // Combine and write unified Swift output let outputSwiftURL = outputDirectory.appending(path: "BridgeJS.swift") let combinedSwift = [closureSupport, exportResult, importResult].compactMap { $0 } - let outputSwift = combineGeneratedSwift(combinedSwift) + let outputSwift = combineGeneratedSwift( + combinedSwift, + importingExternalModules: skeleton.usedExternalModules + ) let shouldWrite = doubleDashOptions["always-write"] == "true" || !outputSwift.isEmpty if shouldWrite { try withSpan("Writing output Swift") { @@ -236,7 +250,14 @@ import BridgeJSUtilities } } - // Write unified skeleton + // Write unified skeleton. + // Note that for the build system to sequence the BridgeJSBuildPlugin correctly, + // the skeleton-to-Swift-output mapping must be injective, i.e. any change to + // the skeleton must produce a change in the Swift output. This is because we + // can’t use the BridgeJS.json file as an outputFile, since it would then be + // treated as a resource and thus included in the generated bundle. The + // invariant currently holds, but if this ever changes the BridgeJS.swift file + // could include a hash of the skeleton to maintain it. let outputSkeletonURL = outputDirectory.appending(path: "JavaScript/BridgeJS.json") try withSpan("Writing output skeleton") { try FileManager.default.createDirectory( @@ -301,14 +322,45 @@ private func writeIfChanged(_ data: Data, to url: URL) throws { try data.write(to: url) } -private func combineGeneratedSwift(_ pieces: [String]) -> String { +private func loadDependencySkeletons( + _ rawArguments: [String] +) throws -> [(moduleName: String, skeleton: BridgeJSSkeleton)] { + var loadedSkeletons: [(moduleName: String, skeleton: BridgeJSSkeleton)] = [] + let decoder = JSONDecoder() + for rawArgument in rawArguments { + guard let equalIndex = rawArgument.firstIndex(of: "="), equalIndex != rawArgument.startIndex else { + throw BridgeJSToolError( + "--dependency-skeleton expects '='; got invalid value '\(rawArgument)'" + ) + } + let moduleName = String(rawArgument[.. String { let trimmedPieces = pieces .map { $0.trimmingCharacters(in: .newlines) } .filter { !$0.isEmpty } guard !trimmedPieces.isEmpty else { return "" } - return ([BridgeJSGeneratedFile.swiftPreamble] + trimmedPieces).joined(separator: "\n\n") + let imports = BridgeJSGeneratedFile.swiftImports(["JavaScriptKit"] + externalModules) + return ([BridgeJSGeneratedFile.swiftHeader, imports] + trimmedPieces).joined(separator: "\n\n") } private func recursivelyCollectSwiftFiles(from directory: URL) -> [URL] { @@ -364,6 +416,7 @@ extension Profiling { struct OptionRule { var help: String var required: Bool = false + var repeatable: Bool = false } struct ArgumentParser { @@ -376,11 +429,12 @@ struct ArgumentParser { self.doubleDashOptions = doubleDashOptions } - typealias ParsedArguments = ( - positionalArguments: [String], - singleDashOptions: [String: String], - doubleDashOptions: [String: String] - ) + struct ParsedArguments { + var positionalArguments: [String] + var singleDashOptions: [String: String] + var doubleDashOptions: [String: String] + var repeatedDoubleDashOptions: [String: [String]] + } func help() -> String { var help = "Usage: \(CommandLine.arguments.first ?? "bridge-js-tool") [options] \n\n" @@ -403,6 +457,7 @@ struct ArgumentParser { var positionalArguments: [String] = [] var singleDashOptions: [String: String] = [:] var doubleDashOptions: [String: String] = [:] + var repeatedDoubleDashOptions: [String: [String]] = [:] var arguments = arguments.makeIterator() @@ -411,7 +466,12 @@ struct ArgumentParser { if arg.starts(with: "--") { let key = String(arg.dropFirst(2)) let value = arguments.next() - doubleDashOptions[key] = value + if self.doubleDashOptions[key]?.repeatable ?? false { + guard let value else { continue } + repeatedDoubleDashOptions[key, default: []].append(value) + } else { + doubleDashOptions[key] = value + } } else { let key = String(arg.dropFirst(1)) let value = arguments.next() @@ -423,11 +483,16 @@ struct ArgumentParser { } for (key, rule) in self.doubleDashOptions { - if rule.required, doubleDashOptions[key] == nil { + if rule.required, doubleDashOptions[key] == nil, repeatedDoubleDashOptions[key] == nil { throw BridgeJSToolError("Option --\(key) is required") } } - return (positionalArguments, singleDashOptions, doubleDashOptions) + return ParsedArguments( + positionalArguments: positionalArguments, + singleDashOptions: singleDashOptions, + doubleDashOptions: doubleDashOptions, + repeatedDoubleDashOptions: repeatedDoubleDashOptions + ) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSToolInternal/BridgeJSToolInternal.swift b/Plugins/BridgeJS/Sources/BridgeJSToolInternal/BridgeJSToolInternal.swift index 8a6a6b7b4..f4de24093 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSToolInternal/BridgeJSToolInternal.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSToolInternal/BridgeJSToolInternal.swift @@ -48,7 +48,8 @@ import ArgumentParser let swiftToSkeleton = SwiftToSkeleton( progress: ProgressReporting(verbose: false), moduleName: "InternalModule", - exposeToGlobal: false + exposeToGlobal: false, + externalModuleIndex: .empty ) for inputFile in inputFiles.sorted() { let content = try String(decoding: readData(from: inputFile), as: UTF8.self) diff --git a/Plugins/BridgeJS/Sources/BridgeJSUtilities/Utilities.swift b/Plugins/BridgeJS/Sources/BridgeJSUtilities/Utilities.swift index 68c07f225..cb6e0d0b0 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSUtilities/Utilities.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSUtilities/Utilities.swift @@ -13,19 +13,22 @@ public enum BridgeJSGeneratedFile { content.starts(with: skipLine + "\n") } - public static var swiftPreamble: String { + public static var swiftHeader: String { // The generated Swift file itself should not be processed by BridgeJS again. """ \(skipLine) + // swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run // `swift package bridge-js`. - - @_spi(BridgeJS) import JavaScriptKit """ } + + public static func swiftImports(_ moduleNames: [String]) -> String { + moduleNames.map { "@_spi(BridgeJS) import \($0)" }.joined(separator: "\n") + } } /// A printer for code fragments. diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/cli.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/cli.js index 17086e92e..c5e89c208 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/cli.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/cli.js @@ -149,6 +149,7 @@ export function run(filePaths, options) { } const prelude = [ + "// swift-format-ignore-file", "// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,", "// DO NOT EDIT.", "//", diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js index 9617a5261..91a42a9ef 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js @@ -313,8 +313,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, decl); - const returnType = this.visitType(signature.getReturnType(), decl); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), decl); + const effects = this.renderEffects({ isAsync }); const annotation = this.renderMacroAnnotation("JSFunction", args); this.emitDocComment(decl, { indent: "", parameterNameMap }); @@ -581,8 +581,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, node); - const returnType = this.visitType(signature.getReturnType(), node); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), node); + const effects = this.renderEffects({ isAsync }); const swiftFuncName = this.renderIdentifier(swiftName); this.emitDocComment(node, { parameterNameMap }); @@ -1210,8 +1210,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, node); - const returnType = this.visitType(signature.getReturnType(), node); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), node); + const effects = this.renderEffects({ isAsync }); const swiftMethodName = this.renderIdentifier(swiftName); const isStatic = node.modifiers?.some( (modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword @@ -1281,6 +1281,27 @@ export class TypeProcessor { return parts.join(" "); } + /** + * Check if a type is Promise and extract the return type and async flag. + * @param {ts.Type} type - The return type to check + * @param {ts.Node} node - The node for type visiting context + * @returns {{ returnType: string, isAsync: boolean }} + * @private + */ + unwrapPromiseReturnType(type, node) { + if (isTypeReference(type)) { + const symbol = type.target?.getSymbol(); + if (symbol?.name === "Promise") { + const typeArgs = this.checker.getTypeArguments(/** @type {ts.TypeReference} */ (type)); + const innerType = typeArgs && typeArgs.length > 0 + ? this.visitType(typeArgs[0], node) + : "Void"; + return { returnType: innerType, isAsync: true }; + } + } + return { returnType: this.visitType(type, node), isAsync: false }; + } + /** * @param {ts.Node} node * @returns {boolean} diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap index 4122f4148..0cb0e3206 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap @@ -1,7 +1,8 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ts2swift > snapshots Swift output for ArrayParameter.d.ts > ArrayParameter 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -24,7 +25,8 @@ exports[`ts2swift > snapshots Swift output for ArrayParameter.d.ts > ArrayParame `; exports[`ts2swift > snapshots Swift output for Async.d.ts > Async 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -32,24 +34,25 @@ exports[`ts2swift > snapshots Swift output for Async.d.ts > Async 1`] = ` @_spi(BridgeJS) import JavaScriptKit -@JSFunction func asyncReturnVoid() throws(JSException) -> JSPromise +@JSFunction func asyncReturnVoid() async throws(JSException) -> Void -@JSFunction func asyncRoundTripInt(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripInt(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripString(_ v: String) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripString(_ v: String) async throws(JSException) -> String -@JSFunction func asyncRoundTripBool(_ v: Bool) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool -@JSFunction func asyncRoundTripFloat(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripFloat(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripDouble(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripJSObject(_ v: JSValue) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripJSObject(_ v: JSValue) async throws(JSException) -> JSValue " `; exports[`ts2swift > snapshots Swift output for CallableConst.d.ts > CallableConst 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -65,7 +68,8 @@ exports[`ts2swift > snapshots Swift output for CallableConst.d.ts > CallableCons `; exports[`ts2swift > snapshots Swift output for Documentation.d.ts > Documentation 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -122,7 +126,8 @@ exports[`ts2swift > snapshots Swift output for Documentation.d.ts > Documentatio `; exports[`ts2swift > snapshots Swift output for ExportAssignment.d.ts > ExportAssignment 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -135,7 +140,8 @@ exports[`ts2swift > snapshots Swift output for ExportAssignment.d.ts > ExportAss `; exports[`ts2swift > snapshots Swift output for Interface.d.ts > Interface 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -153,7 +159,8 @@ exports[`ts2swift > snapshots Swift output for Interface.d.ts > Interface 1`] = `; exports[`ts2swift > snapshots Swift output for InvalidPropertyNames.d.ts > InvalidPropertyNames 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -201,7 +208,8 @@ exports[`ts2swift > snapshots Swift output for InvalidPropertyNames.d.ts > Inval `; exports[`ts2swift > snapshots Swift output for MultipleImportedTypes.d.ts > MultipleImportedTypes 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -238,7 +246,8 @@ exports[`ts2swift > snapshots Swift output for MultipleImportedTypes.d.ts > Mult `; exports[`ts2swift > snapshots Swift output for ObjectLikeTypes.d.ts > ObjectLikeTypes 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -251,7 +260,8 @@ exports[`ts2swift > snapshots Swift output for ObjectLikeTypes.d.ts > ObjectLike `; exports[`ts2swift > snapshots Swift output for OptionalNullUndefined.d.ts > OptionalNullUndefined 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -292,7 +302,8 @@ exports[`ts2swift > snapshots Swift output for OptionalNullUndefined.d.ts > Opti `; exports[`ts2swift > snapshots Swift output for PrimitiveParameters.d.ts > PrimitiveParameters 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -305,7 +316,8 @@ exports[`ts2swift > snapshots Swift output for PrimitiveParameters.d.ts > Primit `; exports[`ts2swift > snapshots Swift output for PrimitiveReturn.d.ts > PrimitiveReturn 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -320,7 +332,8 @@ exports[`ts2swift > snapshots Swift output for PrimitiveReturn.d.ts > PrimitiveR `; exports[`ts2swift > snapshots Swift output for ReExportFrom.d.ts > ReExportFrom 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -338,7 +351,8 @@ exports[`ts2swift > snapshots Swift output for ReExportFrom.d.ts > ReExportFrom `; exports[`ts2swift > snapshots Swift output for RecordDictionary.d.ts > RecordDictionary 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -368,7 +382,8 @@ exports[`ts2swift > snapshots Swift output for RecordDictionary.d.ts > RecordDic `; exports[`ts2swift > snapshots Swift output for StaticProperty.d.ts > StaticProperty 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -384,7 +399,8 @@ exports[`ts2swift > snapshots Swift output for StaticProperty.d.ts > StaticPrope `; exports[`ts2swift > snapshots Swift output for StringEnum.d.ts > StringEnum 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -405,7 +421,8 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} `; exports[`ts2swift > snapshots Swift output for StringLiteralUnion.d.ts > StringLiteralUnion 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -426,7 +443,8 @@ extension Direction: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} `; exports[`ts2swift > snapshots Swift output for StringParameter.d.ts > StringParameter 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -441,7 +459,8 @@ exports[`ts2swift > snapshots Swift output for StringParameter.d.ts > StringPara `; exports[`ts2swift > snapshots Swift output for StringReturn.d.ts > StringReturn 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -454,7 +473,8 @@ exports[`ts2swift > snapshots Swift output for StringReturn.d.ts > StringReturn `; exports[`ts2swift > snapshots Swift output for TS2SkeletonLike.d.ts > TS2SkeletonLike 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -480,7 +500,8 @@ exports[`ts2swift > snapshots Swift output for TS2SkeletonLike.d.ts > TS2Skeleto `; exports[`ts2swift > snapshots Swift output for TypeAlias.d.ts > TypeAlias 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -493,7 +514,8 @@ exports[`ts2swift > snapshots Swift output for TypeAlias.d.ts > TypeAlias 1`] = `; exports[`ts2swift > snapshots Swift output for TypeAliasObject.d.ts > TypeAliasObject 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -512,7 +534,8 @@ exports[`ts2swift > snapshots Swift output for TypeAliasObject.d.ts > TypeAliasO `; exports[`ts2swift > snapshots Swift output for TypeScriptClass.d.ts > TypeScriptClass 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -533,7 +556,8 @@ exports[`ts2swift > snapshots Swift output for TypeScriptClass.d.ts > TypeScript `; exports[`ts2swift > snapshots Swift output for VoidParameterVoidReturn.d.ts > VoidParameterVoidReturn 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run @@ -546,7 +570,8 @@ exports[`ts2swift > snapshots Swift output for VoidParameterVoidReturn.d.ts > Vo `; exports[`ts2swift > snapshots Swift output for WebIDLDOMDocs.d.ts > WebIDLDOMDocs 1`] = ` -"// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +"// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // // To update this file, just rebuild your project or run diff --git a/Plugins/BridgeJS/Tests/BridgeJSMacrosTests/JSClassMacroTests.swift b/Plugins/BridgeJS/Tests/BridgeJSMacrosTests/JSClassMacroTests.swift index 77dc814eb..c52bc9db0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSMacrosTests/JSClassMacroTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSMacrosTests/JSClassMacroTests.swift @@ -445,6 +445,64 @@ import BridgeJSMacros ) } + @Test func nestedJSClassStruct() { + let combinedSpecs: [String: MacroSpec] = [ + "JSClass": MacroSpec(type: JSClassMacro.self, conformances: ["_JSBridgedClass"]), + "JSGetter": MacroSpec(type: JSGetterMacro.self), + ] + TestSupport.assertMacroExpansion( + """ + @JSClass + struct User { + @JSGetter + var stats: Stats + + @JSClass + struct Stats { + @JSGetter + var health: Int + } + } + """, + expandedSource: """ + struct User { + var stats: Stats { + get throws(JSException) { + return try _$User_stats_get(self.jsObject) + } + } + struct Stats { + var health: Int { + get throws(JSException) { + return try _$Stats_health_get(self.jsObject) + } + } + + let jsObject: JSObject + + init(unsafelyWrapping jsObject: JSObject) { + self.jsObject = jsObject + } + } + + let jsObject: JSObject + + init(unsafelyWrapping jsObject: JSObject) { + self.jsObject = jsObject + } + } + + extension User.Stats: _JSBridgedClass { + } + + extension User: _JSBridgedClass { + } + """, + macroSpecs: combinedSpecs, + indentationWidth: indentationWidth + ) + } + @Test func fileprivateStructIsRejected() { TestSupport.assertMacroExpansion( """ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCodegenTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCodegenTests.swift index dd0ce5d03..8b8e8b8a2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCodegenTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCodegenTests.swift @@ -77,7 +77,12 @@ import Testing let url = Self.inputsDirectory.appendingPathComponent(input) let name = url.deletingPathExtension().lastPathComponent let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) swiftAPI.addSourceFile(sourceFile, inputFilePath: input) let skeleton = try swiftAPI.finalize() try snapshotCodegen(skeleton: skeleton, name: name) @@ -93,7 +98,12 @@ import Testing let url = Self.inputsDirectory.appendingPathComponent(input) let name = url.deletingPathExtension().lastPathComponent let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: true) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: true, + externalModuleIndex: .empty + ) swiftAPI.addSourceFile(sourceFile, inputFilePath: input) let skeleton = try swiftAPI.finalize() try snapshotCodegen(skeleton: skeleton, name: name + ".Global") @@ -101,7 +111,12 @@ import Testing @Test func codegenCrossFileTypeResolution() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let classBURL = Self.multifileInputsDirectory.appendingPathComponent("CrossFileClassB.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: classBURL, encoding: .utf8)), @@ -118,7 +133,12 @@ import Testing @Test func codegenCrossFileTypeResolutionReverseOrder() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let classAURL = Self.multifileInputsDirectory.appendingPathComponent("CrossFileClassA.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: classAURL, encoding: .utf8)), @@ -135,7 +155,12 @@ import Testing @Test func codegenCrossFileFunctionTypes() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let functionBURL = Self.multifileInputsDirectory.appendingPathComponent("CrossFileFunctionB.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: functionBURL, encoding: .utf8)), @@ -152,7 +177,12 @@ import Testing @Test func codegenCrossFileFunctionTypesReverseOrder() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let functionAURL = Self.multifileInputsDirectory.appendingPathComponent("CrossFileFunctionA.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: functionAURL, encoding: .utf8)), @@ -169,7 +199,12 @@ import Testing @Test func codegenCrossFileExtension() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let classURL = Self.multifileInputsDirectory.appendingPathComponent("CrossFileExtensionClass.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: classURL, encoding: .utf8)), @@ -186,7 +221,12 @@ import Testing @Test func codegenSkipsEmptySkeletons() throws { - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) let importedURL = Self.multifileInputsDirectory.appendingPathComponent("ImportedFunctions.swift") swiftAPI.addSourceFile( Parser.parse(source: try String(contentsOf: importedURL, encoding: .utf8)), diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift index 711b04512..2f3f46fdb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift @@ -49,7 +49,12 @@ import Testing let name = url.deletingPathExtension().lastPathComponent let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) - let importSwift = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: false) + let importSwift = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) importSwift.addSourceFile(sourceFile, inputFilePath: "\(name).swift") let importResult = try importSwift.finalize() var bridgeJSLink = BridgeJSLink(sharedMemory: false) @@ -69,7 +74,12 @@ import Testing func snapshotExportWithGlobal(inputFile: String) throws { let url = Self.inputsDirectory.appendingPathComponent(inputFile) let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) - let swiftAPI = SwiftToSkeleton(progress: .silent, moduleName: "TestModule", exposeToGlobal: true) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: true, + externalModuleIndex: .empty + ) swiftAPI.addSourceFile(sourceFile, inputFilePath: inputFile) let name = url.deletingPathExtension().lastPathComponent let outputSkeleton = try swiftAPI.finalize() @@ -86,13 +96,23 @@ import Testing func snapshotMixedModuleExposure() throws { let globalURL = Self.inputsDirectory.appendingPathComponent("MixedGlobal.swift") let globalSourceFile = Parser.parse(source: try String(contentsOf: globalURL, encoding: .utf8)) - let globalAPI = SwiftToSkeleton(progress: .silent, moduleName: "GlobalModule", exposeToGlobal: true) + let globalAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "GlobalModule", + exposeToGlobal: true, + externalModuleIndex: .empty + ) globalAPI.addSourceFile(globalSourceFile, inputFilePath: "MixedGlobal.swift") let globalSkeleton = try globalAPI.finalize() let privateURL = Self.inputsDirectory.appendingPathComponent("MixedPrivate.swift") let privateSourceFile = Parser.parse(source: try String(contentsOf: privateURL, encoding: .utf8)) - let privateAPI = SwiftToSkeleton(progress: .silent, moduleName: "PrivateModule", exposeToGlobal: false) + let privateAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "PrivateModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) privateAPI.addSourceFile(privateSourceFile, inputFilePath: "MixedPrivate.swift") let privateSkeleton = try privateAPI.finalize() @@ -105,4 +125,55 @@ import Testing ) try snapshot(bridgeJSLink: bridgeJSLink, name: "MixedModules") } + + @Test + func perClassIdentityModeFromAnnotation() throws { + let url = Self.inputsDirectory.appendingPathComponent("IdentityModeClass.swift") + let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty, + identityMode: nil // no config default + ) + swiftAPI.addSourceFile(sourceFile, inputFilePath: "IdentityModeClass.swift") + let outputSkeleton = try swiftAPI.finalize() + + // Verify skeleton has per-class identity mode (not captured by snapshots) + let cachedClass = outputSkeleton.exported!.classes.first { $0.name == "CachedModel" } + let uncachedClass = outputSkeleton.exported!.classes.first { $0.name == "UncachedModel" } + let explicitlyUncachedClass = outputSkeleton.exported!.classes.first { $0.name == "ExplicitlyUncachedModel" } + #expect(cachedClass?.identityMode == true) + #expect(uncachedClass?.identityMode == nil) + #expect(explicitlyUncachedClass?.identityMode == false) + + // Verify generated JS via snapshot + let bridgeJSLink = BridgeJSLink(skeletons: [outputSkeleton], sharedMemory: false) + try snapshot(bridgeJSLink: bridgeJSLink, name: "IdentityModeClass.PerClass") + } + + @Test + func perClassIdentityModeWithConfigOverride() throws { + let url = Self.inputsDirectory.appendingPathComponent("IdentityModeClass.swift") + let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty, + identityMode: "pointer" // config says pointer for all classes + ) + swiftAPI.addSourceFile(sourceFile, inputFilePath: "IdentityModeClass.swift") + let outputSkeleton = try swiftAPI.finalize() + + // When config says "pointer", classes without annotation get identity mode from config. + // But @JS(identityMode: false) should still override to "without identity". + let explicitlyUncachedClass = outputSkeleton.exported!.classes.first { $0.name == "ExplicitlyUncachedModel" } + #expect(explicitlyUncachedClass?.identityMode == false) + + // Verify generated JS via snapshot + let bridgeJSLink = BridgeJSLink(skeletons: [outputSkeleton], sharedMemory: false) + try snapshot(bridgeJSLink: bridgeJSLink, name: "IdentityModeClass.ConfigPointer") + } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/CrossModuleResolutionTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/CrossModuleResolutionTests.swift new file mode 100644 index 000000000..4f12a88fa --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/CrossModuleResolutionTests.swift @@ -0,0 +1,491 @@ +import Foundation +import SwiftParser +import SwiftSyntax +import Testing + +@testable import BridgeJSCore +@testable import BridgeJSSkeleton + +@Suite struct CrossModuleResolutionTests { + @Test + func resolvesTopLevelExternalStruct() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public struct Vector3D { + public let x: Double + public let y: Double + public let z: Double + @JS public init(x: Double, y: Double, z: Double) { + self.x = x; self.y = y; self.z = z + } + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func currentVelocity() -> Vector3D { + Vector3D(x: 0, y: 0, z: 0) + } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + #expect(app.usedExternalModules == ["Core"]) + let function = try #require(app.exported?.functions.first(where: { $0.name == "currentVelocity" })) + #expect(function.returnType == .swiftStruct("Vector3D")) + } + + @Test + func resolvesTopLevelExternalClass() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public class Emitter { + @JS public init() {} + @JS public func emit() -> String { "" } + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func makeEmitter() -> Emitter { Emitter() } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + #expect(app.usedExternalModules == ["Core"]) + let function = try #require(app.exported?.functions.first(where: { $0.name == "makeEmitter" })) + #expect(function.returnType == .swiftHeapObject("Emitter")) + } + + @Test + func resolvesNestedExternalStruct() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public enum Geometry { + @JS public struct BoundingBox { + public let side: Double + @JS public init(side: Double) { self.side = side } + } + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func unitBox() -> Geometry.BoundingBox { + Geometry.BoundingBox(side: 1) + } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + #expect(app.usedExternalModules == ["Core"]) + let function = try #require(app.exported?.functions.first(where: { $0.name == "unitBox" })) + #expect(function.returnType == .swiftStruct("Geometry.BoundingBox")) + } + + @Test + func resolvesExplicitModuleQualifier() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public struct Vector3D { + @JS public init() {} + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func fromCore() -> Core.Vector3D { Core.Vector3D() } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "fromCore" })) + #expect(function.returnType == .swiftStruct("Vector3D")) + } + + @Test + func resolvesExternalTypeInArrayAndOptional() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public struct Point { + @JS public init() {} + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func scatter(points: [Point?]) -> Point? { nil } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "scatter" })) + #expect(function.returnType == .nullable(.swiftStruct("Point"), .null)) + #expect(function.parameters.first?.type == .array(.nullable(.swiftStruct("Point"), .null))) + } + + // MARK: - Diagnostics + + @Test + func ambiguousExternalNameProducesDiagnostic() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let graphics = try buildDependencySkeleton( + moduleName: "Graphics", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + + do { + _ = try resolveApp( + source: """ + import Core + import Graphics + @JS public func ambiguous() -> Vector3D { fatalError() } + """, + dependencies: [ + (moduleName: "Core", skeleton: core), + (moduleName: "Graphics", skeleton: graphics), + ] + ) + Issue.record("Expected ambiguity diagnostic, but resolution succeeded") + } catch let error as BridgeJSCoreDiagnosticError { + let combined = error.diagnostics.map(\.diagnostic.message).joined(separator: "\n") + #expect(combined.contains("ambiguous use of 'Vector3D'")) + let combinedHints = error.diagnostics.compactMap(\.diagnostic.hint).joined(separator: "\n") + #expect(combinedHints.contains("Core")) + #expect(combinedHints.contains("Graphics")) + } + } + + @Test + func explicitQualifierResolvesAmbiguity() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let graphics = try buildDependencySkeleton( + moduleName: "Graphics", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Core + import Graphics + @JS public func fromCore() -> Core.Vector3D { Core.Vector3D() } + """, + dependencies: [ + (moduleName: "Core", skeleton: core), + (moduleName: "Graphics", skeleton: graphics), + ] + ) + #expect(app.usedExternalModules == ["Core"]) + } + + @Test + func localDeclarationShadowsExternalSameName() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public struct Vector3D { + public let id: Int + @JS public init(id: Int) { self.id = id } + } + @JS public func makeLocal() -> Vector3D { Vector3D(id: 42) } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + // Local declaration wins. + #expect(app.usedExternalModules == []) + let exported = try #require(app.exported) + #expect(exported.structs.contains(where: { $0.name == "Vector3D" })) + } + + @Test + func unknownTypeEmitsHintMentioningDependencyTargets() throws { + do { + _ = try resolveApp( + source: """ + @JS public func use() -> MissingType { fatalError() } + """, + dependencies: [] + ) + Issue.record("Expected an unsupported-type diagnostic") + } catch let error as BridgeJSCoreDiagnosticError { + let combinedHints = error.diagnostics.compactMap(\.diagnostic.hint).joined(separator: "\n") + #expect(combinedHints.contains("dependency targets")) + } + } + + // MARK: - Coverage across type categories + + @Test + func resolvesExternalSimpleEnum() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public enum Direction { case north, south }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public func opposite(_ d: Direction) -> Direction { d } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "opposite" })) + #expect(function.returnType == .caseEnum("Direction")) + #expect(function.parameters.first?.type == .caseEnum("Direction")) + } + + @Test + func resolvesExternalRawValueEnum() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public enum HTTPMethod: String { case get, post }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public func describe(_ m: HTTPMethod) -> String { "" } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "describe" })) + #expect(function.parameters.first?.type == .rawValueEnum("HTTPMethod", .string)) + } + + @Test + func resolvesExternalAssociatedValueEnum() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public enum Shape { case point, circle(radius: Double) }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public func area(_ s: Shape) -> Double { 0 } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "area" })) + #expect(function.parameters.first?.type == .associatedValueEnum("Shape")) + } + + @Test + func resolvesExternalNamespaceEnum() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: """ + @JS public enum Utils { + @JS public static func hello() -> String { "hi" } + } + """ + ) + let app = try resolveApp( + source: """ + import Core + @JS public func dummy(_ u: Utils?) -> Utils? { u } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "dummy" })) + #expect(function.returnType == .nullable(.namespaceEnum("Utils"), .null)) + } + + // MARK: - Structural positions + + @Test + func resolvesExternalInDictionaryValuePosition() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public func names(_ map: [String: Vector3D]) -> [String] { [] } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "names" })) + #expect(function.parameters.first?.type == .dictionary(.swiftStruct("Vector3D"))) + } + + @Test + func resolvesExternalInStructPropertyType() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public struct Particle { + public let position: Vector3D + @JS public init(position: Vector3D) { self.position = position } + } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let particle = try #require(app.exported?.structs.first(where: { $0.name == "Particle" })) + let positionProperty = try #require(particle.properties.first(where: { $0.name == "position" })) + #expect(positionProperty.type == .swiftStruct("Vector3D")) + #expect(app.usedExternalModules == ["Core"]) + } + + // MARK: - Multi-module scenarios + + @Test + func tracksMultipleExternalModulesInSortedOrder() throws { + let alpha = try buildDependencySkeleton( + moduleName: "Alpha", + source: "@JS public struct A { @JS public init() {} }" + ) + let beta = try buildDependencySkeleton( + moduleName: "Beta", + source: "@JS public struct B { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Alpha + import Beta + @JS public func both(_ a: A, _ b: B) -> String { "" } + """, + dependencies: [ + (moduleName: "Beta", skeleton: beta), + (moduleName: "Alpha", skeleton: alpha), + ] + ) + #expect(app.usedExternalModules == ["Alpha", "Beta"]) + } + + @Test + func transitiveDependencyTypesResolve() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let domain = try buildDependencySkeleton( + moduleName: "Domain", + source: """ + @JS public struct Particle { + public let position: Vector3D + @JS public init(position: Vector3D) { self.position = position } + } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + #expect(domain.usedExternalModules == ["Core"]) + // App can still reference Vector3D through Domain’s transitive dependency on Core. + let app = try resolveApp( + source: """ + import Core + import Domain + @JS public func position(of p: Particle) -> Vector3D { p.position } + """, + dependencies: [ + (moduleName: "Core", skeleton: core), + (moduleName: "Domain", skeleton: domain), + ] + ) + let function = try #require(app.exported?.functions.first(where: { $0.name == "position" })) + #expect(function.returnType == .swiftStruct("Vector3D")) + #expect(function.parameters.first?.type == .swiftStruct("Particle")) + #expect(app.usedExternalModules == ["Core", "Domain"]) + } + + // MARK: - Skeleton serialisation round-trip + + @Test + func skeletonRoundTripsUsedExternalModules() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + let app = try resolveApp( + source: """ + import Core + @JS public func origin() -> Vector3D { Vector3D() } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys] + let data = try encoder.encode(app) + let decoded = try JSONDecoder().decode(BridgeJSSkeleton.self, from: data) + #expect(decoded.usedExternalModules == app.usedExternalModules) + #expect(decoded.moduleName == app.moduleName) + } + + @Test + func externalModuleIndexSkipsDependenciesWithoutExportedTypes() throws { + let empty = BridgeJSSkeleton(moduleName: "Empty") + let index = ExternalModuleIndex(dependencies: [(moduleName: "Empty", skeleton: empty)]) + #expect(index.isEmpty) + #expect(!index.isKnownModule("Empty")) + #expect(index.lookup(dotPath: "Whatever") == nil) + } + + @Test + func moduleQualifierRejectsUnknownModule() throws { + let core = try buildDependencySkeleton( + moduleName: "Core", + source: "@JS public struct Vector3D { @JS public init() {} }" + ) + do { + _ = try resolveApp( + source: """ + import Core + @JS public func useFoundation() -> Foundation.URL { fatalError() } + """, + dependencies: [(moduleName: "Core", skeleton: core)] + ) + Issue.record("Expected unsupported-type diagnostic for Foundation.URL") + } catch let error as BridgeJSCoreDiagnosticError { + let combinedMessages = error.diagnostics.map(\.diagnostic.message).joined(separator: "\n") + #expect(combinedMessages.contains("Foundation.URL")) + } + } + + // MARK: - Utillites + + private func resolveApp( + source appSource: String, + dependencies: [(moduleName: String, skeleton: BridgeJSSkeleton)] + ) throws -> BridgeJSSkeleton { + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "App", + exposeToGlobal: false, + externalModuleIndex: ExternalModuleIndex(dependencies: dependencies) + ) + let sourceFile = Parser.parse(source: appSource) + swiftAPI.addSourceFile(sourceFile, inputFilePath: "App.swift") + return try swiftAPI.finalize() + } + + private func buildDependencySkeleton( + moduleName: String, + source: String, + dependencies: [(moduleName: String, skeleton: BridgeJSSkeleton)] = [] + ) throws -> BridgeJSSkeleton { + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: moduleName, + exposeToGlobal: false, + externalModuleIndex: ExternalModuleIndex(dependencies: dependencies) + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "\(moduleName).swift") + return try swiftAPI.finalize() + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift index 500a5db95..e71a1f84e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift @@ -3,6 +3,7 @@ import SwiftSyntax import Testing @testable import BridgeJSCore +@testable import BridgeJSSkeleton @Suite struct DiagnosticsTests { /// Returns the first parameter's type node from a function in the source (the first `@JS func`-like decl), for pinpointing diagnostics. @@ -165,6 +166,145 @@ import Testing #expect(description.contains(":2:")) } + // MARK: - Nested type validation + + @Test + func nestedStructInsideClassSucceeds() throws { + let source = """ + @JS class User { + @JS struct Stats { + var health: Int + } + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + let skeleton = try swiftAPI.finalize() + #expect(skeleton.exported != nil) + let structs = skeleton.exported?.structs ?? [] + #expect(structs.count == 1) + #expect(structs.first?.swiftCallName == "User.Stats") + } + + @Test + func nestedClassInsideStructSucceeds() throws { + let source = """ + @JS struct Container { + var value: Int + @JS class Inner { + } + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + let skeleton = try swiftAPI.finalize() + #expect(skeleton.exported != nil) + let classes = skeleton.exported?.classes ?? [] + #expect(classes.count == 1) + #expect(classes.first?.swiftCallName == "Container.Inner") + } + + @Test + func structInsideEnumNamespaceSucceeds() throws { + let source = """ + @JS enum API { + @JS struct Point { + var x: Double + var y: Double + } + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + let skeleton = try swiftAPI.finalize() + #expect(skeleton.exported != nil) + } + + // MARK: - Struct init order validation + + @Test + func structInitMismatchedOrderProducesDiagnostic() throws { + let source = """ + @JS struct Animal { + var size: Double + var age: Int + + @JS init(age: Int, size: Double) { + self.age = age + self.size = size + } + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + #expect(throws: BridgeJSCoreDiagnosticError.self) { + _ = try swiftAPI.finalize() + } + } + + @Test + func structInitMatchingOrderSucceeds() throws { + let source = """ + @JS struct Point { + var x: Double + var y: Double + + @JS init(x: Double, y: Double) { + self.x = x + self.y = y + } + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + let skeleton = try swiftAPI.finalize() + #expect(skeleton.exported != nil) + } + + @Test + func structWithoutExplicitInitSucceeds() throws { + let source = """ + @JS struct Point { + var x: Double + var y: Double + } + """ + let swiftAPI = SwiftToSkeleton( + progress: .silent, + moduleName: "TestModule", + exposeToGlobal: false, + externalModuleIndex: .empty + ) + swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "test.swift") + let skeleton = try swiftAPI.finalize() + #expect(skeleton.exported != nil) + } + @Test func omitsNextLineWhenErrorIsOnLastLine() throws { let source = """ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift new file mode 100644 index 000000000..02563cbdf --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift @@ -0,0 +1,6 @@ +@JSFunction func asyncReturnVoid() async throws(JSException) +@JSFunction func asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int +@JSFunction func asyncRoundTripString(_ v: String) async throws(JSException) -> String +@JSFunction func asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool +@JSFunction func asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double +@JSFunction func asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift new file mode 100644 index 000000000..1afd758bf --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift @@ -0,0 +1,4 @@ +@JSClass struct AsyncBox { + @JSFunction static func asyncStaticRoundTrip(_ v: Double) async throws(JSException) -> Double + @JSFunction static func asyncStaticVoid() async throws(JSException) +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/DictionaryTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/DictionaryTypes.swift index c699ea79b..a45bf1dd8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/DictionaryTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/DictionaryTypes.swift @@ -6,10 +6,16 @@ } } +@JS struct Counters { + var name: String + var counts: [String: Int?] +} + @JS func mirrorDictionary(_ values: [String: Int]) -> [String: Int] @JS func optionalDictionary(_ values: [String: String]?) -> [String: String]? @JS func nestedDictionary(_ values: [String: [Int]]) -> [String: [Int]] @JS func boxDictionary(_ boxes: [String: Box]) -> [String: Box] @JS func optionalBoxDictionary(_ boxes: [String: Box?]) -> [String: Box?] +@JS func roundtripCounters(_ counters: Counters) -> Counters @JSFunction func importMirrorDictionary(_ values: [String: Double]) throws(JSException) -> [String: Double] diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/EnumCaseImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/EnumCaseImport.swift new file mode 100644 index 000000000..a6477be95 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/EnumCaseImport.swift @@ -0,0 +1,12 @@ +@JS enum Signal { + case start + case stop +} + +// Case enums (no raw value) bridge as their `Int32` tag as imported-function +// parameters and return values. +@JSClass struct SignalControls { + @JSFunction func send(_ signal: Signal) throws(JSException) + @JSFunction func current() throws(JSException) -> Signal + @JSFunction static func roundTrip(_ signal: Signal) throws(JSException) -> Signal +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/IdentityModeClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/IdentityModeClass.swift new file mode 100644 index 000000000..4d50b6c76 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/IdentityModeClass.swift @@ -0,0 +1,28 @@ +import JavaScriptKit + +@JS(identityMode: true) +class CachedModel { + @JS var name: String + + @JS init(name: String) { + self.name = name + } +} + +@JS +class UncachedModel { + @JS var value: Int + + @JS init(value: Int) { + self.value = value + } +} + +@JS(identityMode: false) +class ExplicitlyUncachedModel { + @JS var count: Int + + @JS init(count: Int) { + self.count = count + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/JSTypedArrayTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/JSTypedArrayTypes.swift new file mode 100644 index 000000000..7f308f560 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/JSTypedArrayTypes.swift @@ -0,0 +1,19 @@ +import JavaScriptKit + +// Using typealiases +@JS func processBytes(_ data: JSUint8Array) -> JSUint8Array { + return data +} + +@JS func processFloats(_ data: JSFloat32Array) -> JSFloat32Array { + return data +} + +// Using generic form directly +@JS func processGenericDoubles(_ data: JSTypedArray) -> JSTypedArray { + return data +} + +@JS func processGenericInts(_ data: JSTypedArray) -> JSTypedArray { + return data +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Namespaces.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Namespaces.swift index cbe146ff5..7cd63c698 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Namespaces.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Namespaces.swift @@ -16,6 +16,17 @@ func changeName(name: String) { self.name = name } + + // Static methods and properties on a namespaced class must land on the + // class's namespace entry (alongside `new`), not on the instance + // interface and not silently dropped. Regression test for the + // `@JS(namespace:)` + `@JS static func` bug where the hierarchical + // exports builder only emitted the constructor. + @JS static func makeDefault() -> Greeter { + return Greeter(name: "World") + } + + @JS static var defaultGreeting: String { "Hello, world!" } } @JS(namespace: "Utils.Converters") class Converter { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/NestedType.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/NestedType.swift new file mode 100644 index 000000000..12fccb379 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/NestedType.swift @@ -0,0 +1,21 @@ +@JS class User { + @JS func getName() -> String { + return "test" + } + + @JS struct Stats { + var health: Int + var score: Double + } +} + +@JS class Player { + @JS func getTag() -> String { + return "player" + } + + @JS struct Stats { + var level: Int + var rating: String + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift index 927fac6a0..5df48d9c0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift @@ -155,4 +155,10 @@ func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active @JSSetter func setIntOrUndefined(_ value: JSUndefinedOr) throws(JSException) @JSFunction func roundTripIntOrNull(value: Int?) throws(JSException) -> Int? @JSFunction func roundTripIntOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + + @JSGetter var childOrNull: WithOptionalJSClass? + @JSSetter func setChildOrNull(_ value: WithOptionalJSClass?) throws(JSException) + @JSFunction func roundTripChildOrNull( + value: WithOptionalJSClass? + ) throws(JSException) -> WithOptionalJSClass? } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Protocol.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Protocol.swift index fbbad0615..bdb700d69 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Protocol.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Protocol.swift @@ -102,8 +102,12 @@ import JavaScriptKit @JS var delegates: [MyViewControllerDelegate] + @JS + var delegatesByName: [String: MyViewControllerDelegate] + @JS init(delegates: [MyViewControllerDelegate]) { self.delegates = delegates + self.delegatesByName = [:] } @JS func notifyAll() { @@ -114,3 +118,7 @@ import JavaScriptKit } @JS func processDelegates(_ delegates: [MyViewControllerDelegate]) -> [MyViewControllerDelegate] + +@JS func processDelegatesByName( + _ delegates: [String: MyViewControllerDelegate] +) -> [String: MyViewControllerDelegate] diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftClosure.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftClosure.swift index 791b1b7a9..6872d7989 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftClosure.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftClosure.swift @@ -8,10 +8,21 @@ import JavaScriptKit } } +@JS public struct Animal { + public let type: String + + @JS public init(type: String) { + self.type = type + } +} + @JS class TestProcessor { @JS init(transform: @escaping (String) -> String) {} } +@JS func roundtripAnimal(_ animalClosure: (Animal) -> Animal) -> (Animal) -> Animal +@JS func roundtripOptionalAnimal(_ animalClosure: (Animal?) -> Animal?) -> (Animal?) -> Animal? + @JS func roundtripString(_ stringClosure: (String) -> String) -> (String) -> String @JS func roundtripInt(_ intClosure: (Int) -> Int) -> (Int) -> Int @JS func roundtripBool(_ boolClosure: (Bool) -> Bool) -> (Bool) -> Bool diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftStructImports.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftStructImports.swift index b00fd768a..a1eed686a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftStructImports.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftStructImports.swift @@ -5,3 +5,5 @@ struct Point { } @JSFunction func translate(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point + +@JSFunction func roundTripOptional(_ point: Point?) throws(JSException) -> Point? diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift new file mode 100644 index 000000000..6487d343b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift @@ -0,0 +1,26 @@ +// Verifies that `JSTypedClosure` initializers synthesized by BridgeJS adopt the +// access level of the originating `@JSClass`/`@JSFunction` surface, so that +// downstream targets can construct typed closures for public APIs (issue #709). + +@JSClass(jsName: "PublicEvent") public struct JSPublicEvent {} +@JSClass(jsName: "PackageEvent") package struct JSPackageEvent {} +@JSClass(jsName: "InternalEvent") struct JSInternalEvent {} + +@JSClass(jsName: "PublicTarget") public struct JSPublicTarget { + // A public method taking a typed closure must yield a `public` synthesized init, + // since downstream modules may construct the closure value. + @JSFunction public func addPublicListener(_ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) + // Same closure shape on an internal method — the synthesized init merges to public, + // because at most one extension per signature is generated. + @JSFunction func addInternalListener(_ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) +} + +@JSClass(jsName: "PackageTarget") package struct JSPackageTarget { + // A package-level surface yields a `package` synthesized init. + @JSFunction package func addPackageListener(_ handler: JSTypedClosure<(JSPackageEvent) -> Void>) throws(JSException) +} + +@JSClass(jsName: "InternalTarget") struct JSInternalTarget { + // No public/package surface for this signature — the synthesized init stays internal. + @JSFunction func addInternalListener(_ handler: JSTypedClosure<(JSInternalEvent) -> Void>) throws(JSException) +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json index 3664fa339..d4ac7a15f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json @@ -1331,6 +1331,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkArray", "parameters" : [ { @@ -1349,6 +1355,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkArrayWithLength", "parameters" : [ { @@ -1375,6 +1387,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessNumbers", "parameters" : [ { @@ -1397,6 +1415,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importGetNumbers", "parameters" : [ @@ -1412,6 +1436,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importTransformNumbers", "parameters" : [ { @@ -1438,6 +1468,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessStrings", "parameters" : [ { @@ -1464,6 +1500,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessBooleans", "parameters" : [ { @@ -1496,5 +1538,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift index cf65635d9..a058d13a7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift @@ -12,7 +12,7 @@ extension Direction: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -27,7 +27,7 @@ extension Direction: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Async.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Async.json index a2b95cc24..27ba89aca 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Async.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Async.json @@ -189,5 +189,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json new file mode 100644 index 000000000..7f66bede4 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json @@ -0,0 +1,160 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncReturnVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripInt", + "parameters" : [ + { + "name" : "v", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripString", + "parameters" : [ + { + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripBool", + "parameters" : [ + { + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripDouble", + "parameters" : [ + { + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripJSObject", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + + } + } + } + ], + "types" : [ + + ] + } + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift new file mode 100644 index 000000000..7a60bc6b7 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -0,0 +1,569 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules7JSValueV_y") +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules7JSValueV_y") +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModules7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModules7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModules7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +public func _invoke_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules8JSObjectC_y") +fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules8JSObjectC_y") +fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModules8JSObjectC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSObject) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModules8JSObjectC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSObject) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSObject) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModules8JSObjectC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules8JSObjectC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules8JSObjectC_y") +public func _invoke_swift_closure_TestModule_10TestModules8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSObject) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSObject.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSS_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSS_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSS_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending String) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_TestModule_10TestModulesSS_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending String) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSS_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSS_y") +public func _invoke_swift_closure_TestModule_10TestModulesSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSb_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSb_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSb_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSb_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Bool) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModulesSb_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Bool) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSb_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSb_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSb_y") +public func _invoke_swift_closure_TestModule_10TestModulesSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Bool.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSd_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModulesSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSd_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModulesSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSd_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSd_y") +public func _invoke_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSi_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSi_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSi_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSi_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSi_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSi_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Int) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModulesSi_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Int) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Int) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSi_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSi_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSi_y") +public func _invoke_swift_closure_TestModule_10TestModulesSi_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Int) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Int.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuley_y") +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuley_y(_ callback: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuley_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuley_y") +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuley_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuley_y { + static func bridgeJSLift(_ callbackId: Int32) -> () -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuley_y(callbackValue) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == () -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping () -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuley_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuley_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuley_y") +public func _invoke_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<() -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncReturnVoid") +fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncReturnVoid(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_asyncReturnVoid_extern(resolveRef, rejectRef) +} + +func _$asyncReturnVoid() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_asyncReturnVoid(resolveRef, rejectRef) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripInt") +fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void +#else +fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripInt(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripInt_extern(resolveRef, rejectRef, v) +} + +func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Int) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripInt(resolveRef, rejectRef, vValue) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripString") +fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripString(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_asyncRoundTripString_extern(resolveRef, rejectRef, vBytes, vLength) +} + +func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending String) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) + } + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripBool") +fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void +#else +fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripBool(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripBool_extern(resolveRef, rejectRef, v) +} + +func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Bool) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripBool(resolveRef, rejectRef, vValue) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripDouble") +fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void +#else +fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_asyncRoundTripDouble_extern(resolveRef, rejectRef, v) +} + +func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripDouble(resolveRef, rejectRef, vValue) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripJSObject") +fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void +#else +fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripJSObject_extern(resolveRef, rejectRef, v) +} + +func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending JSObject) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripJSObject(resolveRef, rejectRef, vValue) + } + return resolved +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json new file mode 100644 index 000000000..2aea1c115 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json @@ -0,0 +1,73 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "internal", + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "AsyncBox", + "setters" : [ + + ], + "staticMethods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncStaticRoundTrip", + "parameters" : [ + { + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncStaticVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ] + } + ] + } + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift new file mode 100644 index 000000000..ee7dc73e7 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift @@ -0,0 +1,227 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules7JSValueV_y") +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules7JSValueV_y") +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModules7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModules7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModules7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +public func _invoke_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSd_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModulesSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSd_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModulesSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSd_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSd_y") +public func _invoke_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuley_y") +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuley_y(_ callback: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuley_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuley_y") +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuley_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuley_y { + static func bridgeJSLift(_ callbackId: Int32) -> () -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuley_y(callbackValue) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == () -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping () -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuley_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuley_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuley_y") +public func _invoke_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<() -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_AsyncBox_asyncStaticRoundTrip_static") +fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void +#else +fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_AsyncBox_asyncStaticRoundTrip_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_AsyncBox_asyncStaticVoid_static") +fileprivate func bjs_AsyncBox_asyncStaticVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncBox_asyncStaticVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncBox_asyncStaticVoid_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncBox_asyncStaticVoid_static_extern(resolveRef, rejectRef) +} + +func _$AsyncBox_asyncStaticRoundTrip(_ v: Double) async throws(JSException) -> Double { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncBox_asyncStaticRoundTrip_static(resolveRef, rejectRef, vValue) + } + return resolved +} + +func _$AsyncBox_asyncStaticVoid() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_AsyncBox_asyncStaticVoid_static(resolveRef, rejectRef) + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.json index f77d39ad9..4c8d575b0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.json @@ -78,5 +78,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.swift index ab73df508..521e8b595 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileExtension.swift @@ -41,10 +41,13 @@ public func _bjs_Greeter_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { #endif } -extension Greeter: ConvertibleToJSValue, _BridgedSwiftHeapObject { +extension Greeter: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { var jsValue: JSValue { return .object(JSObject(id: UInt32(bitPattern: _bjs_Greeter_wrap(Unmanaged.passRetained(self).toOpaque())))) } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_Greeter_wrap(Unmanaged.passRetained(self).toOpaque()) + } } #if arch(wasm32) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.ReverseOrder.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.ReverseOrder.json index 9b056b650..6c589de87 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.ReverseOrder.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.ReverseOrder.json @@ -148,5 +148,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.json index d76a1622d..9bec040f1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.json @@ -148,5 +148,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json index 4d7495a7c..ff90a4cab 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "fetchNumber", "parameters" : [ @@ -24,5 +30,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json index 59fb8484a..edf8177c1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json @@ -61,5 +61,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json index cc10331a4..58bfadab7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json @@ -61,5 +61,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json index f8a23c33d..e7874c072 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json @@ -1312,5 +1312,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.swift index 4c527315b..507827646 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.swift @@ -12,7 +12,7 @@ extension Status: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .active @@ -25,7 +25,7 @@ extension Status: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .active: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json index c740dc46f..b1185c644 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json @@ -221,13 +221,78 @@ } } } + }, + { + "abiName" : "bjs_roundtripCounters", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundtripCounters", + "parameters" : [ + { + "label" : "_", + "name" : "counters", + "type" : { + "swiftStruct" : { + "_0" : "Counters" + } + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "Counters" + } + } } ], "protocols" : [ ], "structs" : [ + { + "methods" : [ + + ], + "name" : "Counters", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "counts", + "type" : { + "dictionary" : { + "_0" : { + "nullable" : { + "_0" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + }, + "_1" : "null" + } + } + } + } + } + ], + "swiftCallName" : "Counters" + } ] }, "imported" : { @@ -235,6 +300,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importMirrorDictionary", "parameters" : [ { @@ -267,5 +338,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.swift index dc0a54bcf..26a4c087e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.swift @@ -1,3 +1,57 @@ +extension Counters: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> Counters { + let counts = [String: Optional].bridgeJSStackPop() + let name = String.bridgeJSStackPop() + return Counters(name: name, counts: counts) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.name.bridgeJSStackPush() + for __bjs_kv_counts in self.counts { + let __bjs_key_counts = __bjs_kv_counts.key + let __bjs_value_counts = __bjs_kv_counts.value + __bjs_key_counts.bridgeJSStackPush() + __bjs_value_counts.bridgeJSStackPush() + } + _swift_js_push_i32(Int32(self.counts.count)) + } + + init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_Counters(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_Counters())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_Counters") +fileprivate func _bjs_struct_lower_Counters_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_Counters_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_Counters(_ objectId: Int32) -> Void { + return _bjs_struct_lower_Counters_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_Counters") +fileprivate func _bjs_struct_lift_Counters_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_Counters_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_Counters() -> Int32 { + return _bjs_struct_lift_Counters_extern() +} + @_expose(wasm, "bjs_mirrorDictionary") @_cdecl("bjs_mirrorDictionary") public func _bjs_mirrorDictionary() -> Void { @@ -53,6 +107,17 @@ public func _bjs_optionalBoxDictionary() -> Void { #endif } +@_expose(wasm, "bjs_roundtripCounters") +@_cdecl("bjs_roundtripCounters") +public func _bjs_roundtripCounters() -> Void { + #if arch(wasm32) + let ret = roundtripCounters(_: Counters.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Box_deinit") @_cdecl("bjs_Box_deinit") public func _bjs_Box_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json index b92cee954..873c5c49f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json @@ -1455,5 +1455,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.swift index 1112fb5a4..6d5549699 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.swift @@ -203,7 +203,7 @@ extension CardinalDirection: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -218,7 +218,7 @@ extension CardinalDirection: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json index ea32ad739..c4095b502 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json @@ -323,5 +323,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.swift index 06c3e1cd8..66692ee14 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.swift @@ -12,7 +12,7 @@ extension Direction: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -27,7 +27,7 @@ extension Direction: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -55,7 +55,7 @@ extension Status: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .loading @@ -68,7 +68,7 @@ extension Status: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .loading: return 0 @@ -94,7 +94,7 @@ extension TSDirection: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -109,7 +109,7 @@ extension TSDirection: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -137,7 +137,7 @@ extension PublicStatus: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .success @@ -146,7 +146,7 @@ extension PublicStatus: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .success: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.json new file mode 100644 index 000000000..71bf8679e --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.json @@ -0,0 +1,139 @@ +{ + "exported" : { + "classes" : [ + + ], + "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "start" + }, + { + "associatedValues" : [ + + ], + "name" : "stop" + } + ], + "emitStyle" : "const", + "name" : "Signal", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Signal", + "tsFullPath" : "Signal" + } + ], + "exposeToGlobal" : false, + "functions" : [ + + ], + "protocols" : [ + + ], + "structs" : [ + + ] + }, + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "internal", + "getters" : [ + + ], + "methods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "send", + "parameters" : [ + { + "name" : "signal", + "type" : { + "caseEnum" : { + "_0" : "Signal" + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "current", + "parameters" : [ + + ], + "returnType" : { + "caseEnum" : { + "_0" : "Signal" + } + } + } + ], + "name" : "SignalControls", + "setters" : [ + + ], + "staticMethods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "roundTrip", + "parameters" : [ + { + "name" : "signal", + "type" : { + "caseEnum" : { + "_0" : "Signal" + } + } + } + ], + "returnType" : { + "caseEnum" : { + "_0" : "Signal" + } + } + } + ] + } + ] + } + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.swift new file mode 100644 index 000000000..3487ad425 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCaseImport.swift @@ -0,0 +1,97 @@ +extension Signal: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> Signal { + return bridgeJSLiftParameter(value) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> Signal { + return Signal(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSLowerParameter() + } + + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .start + case 1: + self = .stop + default: + return nil + } + } + + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { + switch self { + case .start: + return 0 + case .stop: + return 1 + } + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_roundTrip_static") +fileprivate func bjs_SignalControls_roundTrip_static_extern(_ signal: Int32) -> Int32 +#else +fileprivate func bjs_SignalControls_roundTrip_static_extern(_ signal: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_SignalControls_roundTrip_static(_ signal: Int32) -> Int32 { + return bjs_SignalControls_roundTrip_static_extern(signal) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_send") +fileprivate func bjs_SignalControls_send_extern(_ self: Int32, _ signal: Int32) -> Void +#else +fileprivate func bjs_SignalControls_send_extern(_ self: Int32, _ signal: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_SignalControls_send(_ self: Int32, _ signal: Int32) -> Void { + return bjs_SignalControls_send_extern(self, signal) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_current") +fileprivate func bjs_SignalControls_current_extern(_ self: Int32) -> Int32 +#else +fileprivate func bjs_SignalControls_current_extern(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_SignalControls_current(_ self: Int32) -> Int32 { + return bjs_SignalControls_current_extern(self) +} + +func _$SignalControls_roundTrip(_ signal: Signal) throws(JSException) -> Signal { + let signalValue = signal.bridgeJSLowerParameter() + let ret = bjs_SignalControls_roundTrip_static(signalValue) + if let error = _swift_js_take_exception() { + throw error + } + return Signal.bridgeJSLiftReturn(ret) +} + +func _$SignalControls_send(_ self: JSObject, _ signal: Signal) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let signalValue = signal.bridgeJSLowerParameter() + bjs_SignalControls_send(selfValue, signalValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$SignalControls_current(_ self: JSObject) throws(JSException) -> Signal { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_SignalControls_current(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Signal.bridgeJSLiftReturn(ret) +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.json index 46dbe8917..103a67999 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.json @@ -639,5 +639,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.swift index 1a8608246..5bde4ff93 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.swift @@ -12,7 +12,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -27,7 +27,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 @@ -61,7 +61,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -72,7 +72,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.json index a57703b09..f9890d36b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.json @@ -639,5 +639,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.swift index 1a8608246..5bde4ff93 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.swift @@ -12,7 +12,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -27,7 +27,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 @@ -61,7 +61,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -72,7 +72,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json index ba36405ba..1cf99cd39 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json @@ -1526,6 +1526,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "takesFeatureFlag", "parameters" : [ { @@ -1545,6 +1551,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "returnsFeatureFlag", "parameters" : [ @@ -1563,5 +1575,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json index bef9cbc88..1186ad27d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json @@ -269,6 +269,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt8", "parameters" : [ { @@ -293,6 +299,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt8", "parameters" : [ { @@ -317,6 +329,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt16", "parameters" : [ { @@ -341,6 +359,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt16", "parameters" : [ { @@ -365,6 +389,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt32", "parameters" : [ { @@ -389,6 +419,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt32", "parameters" : [ { @@ -413,6 +449,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt64", "parameters" : [ { @@ -437,6 +479,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt64", "parameters" : [ { @@ -467,5 +515,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json index 55ac7dd70..83353291c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json @@ -7,6 +7,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "name" : "console", "type" : { "jsObject" : { @@ -17,11 +18,18 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "log", "parameters" : [ { @@ -52,5 +60,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json index 5e002e34f..5f6a08da9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "jsName" : "parseInt", "name" : "parseInt", @@ -26,6 +32,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "from" : "global", "name" : "console", "type" : { @@ -37,11 +44,18 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "log", "parameters" : [ { @@ -69,7 +83,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "url", @@ -87,6 +103,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "close", "parameters" : [ @@ -110,5 +132,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.json new file mode 100644 index 000000000..f4a4440c6 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.json @@ -0,0 +1,151 @@ +{ + "exported" : { + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_CachedModel_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + } + ] + }, + "identityMode" : true, + "methods" : [ + + ], + "name" : "CachedModel", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "CachedModel" + }, + { + "constructor" : { + "abiName" : "bjs_UncachedModel_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "UncachedModel", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "value", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "UncachedModel" + }, + { + "constructor" : { + "abiName" : "bjs_ExplicitlyUncachedModel_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "count", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ] + }, + "identityMode" : false, + "methods" : [ + + ], + "name" : "ExplicitlyUncachedModel", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "ExplicitlyUncachedModel" + } + ], + "enums" : [ + + ], + "exposeToGlobal" : false, + "functions" : [ + + ], + "protocols" : [ + + ], + "structs" : [ + + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.swift new file mode 100644 index 000000000..a79b91d56 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/IdentityModeClass.swift @@ -0,0 +1,188 @@ +@_expose(wasm, "bjs_CachedModel_init") +@_cdecl("bjs_CachedModel_init") +public func _bjs_CachedModel_init(_ nameBytes: Int32, _ nameLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = CachedModel(name: String.bridgeJSLiftParameter(nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_CachedModel_name_get") +@_cdecl("bjs_CachedModel_name_get") +public func _bjs_CachedModel_name_get(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = CachedModel.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_CachedModel_name_set") +@_cdecl("bjs_CachedModel_name_set") +public func _bjs_CachedModel_name_set(_ _self: UnsafeMutableRawPointer, _ valueBytes: Int32, _ valueLength: Int32) -> Void { + #if arch(wasm32) + CachedModel.bridgeJSLiftParameter(_self).name = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_CachedModel_deinit") +@_cdecl("bjs_CachedModel_deinit") +public func _bjs_CachedModel_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension CachedModel: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_CachedModel_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_CachedModel_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_CachedModel_wrap") +fileprivate func _bjs_CachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_CachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_CachedModel_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_CachedModel_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_UncachedModel_init") +@_cdecl("bjs_UncachedModel_init") +public func _bjs_UncachedModel_init(_ value: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = UncachedModel(value: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_UncachedModel_value_get") +@_cdecl("bjs_UncachedModel_value_get") +public func _bjs_UncachedModel_value_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = UncachedModel.bridgeJSLiftParameter(_self).value + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_UncachedModel_value_set") +@_cdecl("bjs_UncachedModel_value_set") +public func _bjs_UncachedModel_value_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + UncachedModel.bridgeJSLiftParameter(_self).value = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_UncachedModel_deinit") +@_cdecl("bjs_UncachedModel_deinit") +public func _bjs_UncachedModel_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension UncachedModel: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_UncachedModel_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_UncachedModel_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_UncachedModel_wrap") +fileprivate func _bjs_UncachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_UncachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_UncachedModel_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_UncachedModel_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_ExplicitlyUncachedModel_init") +@_cdecl("bjs_ExplicitlyUncachedModel_init") +public func _bjs_ExplicitlyUncachedModel_init(_ count: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ExplicitlyUncachedModel(count: Int.bridgeJSLiftParameter(count)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ExplicitlyUncachedModel_count_get") +@_cdecl("bjs_ExplicitlyUncachedModel_count_get") +public func _bjs_ExplicitlyUncachedModel_count_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ExplicitlyUncachedModel.bridgeJSLiftParameter(_self).count + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ExplicitlyUncachedModel_count_set") +@_cdecl("bjs_ExplicitlyUncachedModel_count_set") +public func _bjs_ExplicitlyUncachedModel_count_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + ExplicitlyUncachedModel.bridgeJSLiftParameter(_self).count = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ExplicitlyUncachedModel_deinit") +@_cdecl("bjs_ExplicitlyUncachedModel_deinit") +public func _bjs_ExplicitlyUncachedModel_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension ExplicitlyUncachedModel: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_ExplicitlyUncachedModel_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_ExplicitlyUncachedModel_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_ExplicitlyUncachedModel_wrap") +fileprivate func _bjs_ExplicitlyUncachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_ExplicitlyUncachedModel_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_ExplicitlyUncachedModel_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_ExplicitlyUncachedModel_wrap_extern(pointer) +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json index 7f79a8146..7bf447ad5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundtrip", "parameters" : [ { @@ -36,6 +42,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "logStrings", "parameters" : [ { @@ -64,5 +76,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json index f57e77d21..600ae8c89 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json @@ -175,7 +175,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -198,5 +200,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json index 935f7a7f2..7cc1d81a8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createWeirdObject", "parameters" : [ @@ -15,6 +21,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createWeirdClass", "parameters" : [ @@ -28,8 +40,10 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ { + "accessLevel" : "internal", "name" : "normalProperty", "type" : { "string" : { @@ -38,6 +52,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "property-with-dashes", "name" : "property_with_dashes", "type" : { @@ -47,6 +62,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "123invalidStart", "name" : "_123invalidStart", "type" : { @@ -56,6 +72,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "property with spaces", "name" : "property_with_spaces", "type" : { @@ -65,6 +82,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "@specialChar", "name" : "_specialChar", "type" : { @@ -74,6 +92,7 @@ } }, { + "accessLevel" : "internal", "name" : "constructor", "type" : { "string" : { @@ -82,6 +101,7 @@ } }, { + "accessLevel" : "internal", "name" : "for", "type" : { "string" : { @@ -90,6 +110,7 @@ } }, { + "accessLevel" : "internal", "name" : "Any", "type" : { "string" : { @@ -100,6 +121,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "as", "parameters" : [ @@ -111,6 +138,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "try", "parameters" : [ @@ -125,6 +158,7 @@ "name" : "WeirdNaming", "setters" : [ { + "accessLevel" : "internal", "functionName" : "normalProperty_set", "name" : "normalProperty", "type" : { @@ -134,6 +168,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "property_with_dashes_set", "jsName" : "property-with-dashes", "name" : "property_with_dashes", @@ -144,6 +179,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "_123invalidStart_set", "jsName" : "123invalidStart", "name" : "_123invalidStart", @@ -154,6 +190,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "property_with_spaces_set", "jsName" : "property with spaces", "name" : "property_with_spaces", @@ -164,6 +201,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "_specialChar_set", "jsName" : "@specialChar", "name" : "_specialChar", @@ -174,6 +212,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "constructor_set", "name" : "constructor", "type" : { @@ -183,6 +222,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "for_set", "name" : "for", "type" : { @@ -192,6 +232,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "any_set", "jsName" : "Any", "name" : "any", @@ -207,7 +248,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -218,6 +261,12 @@ "jsName" : "$Weird", "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "method-with-dashes", "name" : "method_with_dashes", "parameters" : [ @@ -242,5 +291,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json index 689e86150..2455e5e6d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "returnAnimatable", "parameters" : [ @@ -17,7 +23,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -31,6 +39,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -39,6 +48,7 @@ } }, { + "accessLevel" : "internal", "name" : "age", "type" : { "double" : { @@ -49,6 +59,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "greet", "parameters" : [ @@ -60,6 +76,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "changeName", "parameters" : [ { @@ -81,6 +103,7 @@ "name" : "Greeter", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -95,11 +118,18 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "animate", "parameters" : [ { @@ -126,6 +156,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "getAnimations", "parameters" : [ { @@ -156,5 +192,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json index a8b64558f..363e8d875 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json @@ -7,11 +7,18 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -29,6 +36,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { @@ -47,6 +60,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -58,6 +77,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeDefault", "parameters" : [ @@ -69,6 +94,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "with-dashes", "name" : "dashed", "parameters" : [ @@ -83,7 +114,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -107,6 +140,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { @@ -130,5 +169,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.json new file mode 100644 index 000000000..a7b9c8623 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.json @@ -0,0 +1,123 @@ +{ + "exported" : { + "classes" : [ + + ], + "enums" : [ + + ], + "exposeToGlobal" : false, + "functions" : [ + { + "abiName" : "bjs_processBytes", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processBytes", + "parameters" : [ + { + "label" : "_", + "name" : "data", + "type" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + }, + { + "abiName" : "bjs_processFloats", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processFloats", + "parameters" : [ + { + "label" : "_", + "name" : "data", + "type" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + } + }, + { + "abiName" : "bjs_processGenericDoubles", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processGenericDoubles", + "parameters" : [ + { + "label" : "_", + "name" : "data", + "type" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + } + }, + { + "abiName" : "bjs_processGenericInts", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processGenericInts", + "parameters" : [ + { + "label" : "_", + "name" : "data", + "type" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + } + } + ], + "protocols" : [ + + ], + "structs" : [ + + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.swift new file mode 100644 index 000000000..4777af058 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSTypedArrayTypes.swift @@ -0,0 +1,43 @@ +@_expose(wasm, "bjs_processBytes") +@_cdecl("bjs_processBytes") +public func _bjs_processBytes(_ data: Int32) -> Int32 { + #if arch(wasm32) + let ret = processBytes(_: JSUint8Array.bridgeJSLiftParameter(data)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_processFloats") +@_cdecl("bjs_processFloats") +public func _bjs_processFloats(_ data: Int32) -> Int32 { + #if arch(wasm32) + let ret = processFloats(_: JSFloat32Array.bridgeJSLiftParameter(data)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_processGenericDoubles") +@_cdecl("bjs_processGenericDoubles") +public func _bjs_processGenericDoubles(_ data: Int32) -> Int32 { + #if arch(wasm32) + let ret = processGenericDoubles(_: JSFloat64Array.bridgeJSLiftParameter(data)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_processGenericInts") +@_cdecl("bjs_processGenericInts") +public func _bjs_processGenericInts(_ data: Int32) -> Int32 { + #if arch(wasm32) + let ret = processGenericInts(_: JSInt32Array.bridgeJSLiftParameter(data)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json index 5bd83be27..f0cd29565 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json @@ -321,6 +321,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsEchoJSValue", "parameters" : [ { @@ -339,6 +345,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsEchoJSValueArray", "parameters" : [ { @@ -371,5 +383,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedGlobal.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedGlobal.json index f140fd007..0d30063ee 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedGlobal.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedGlobal.json @@ -75,5 +75,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedPrivate.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedPrivate.json index 9e9dc445e..e6bcf2e5c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedPrivate.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/MixedPrivate.json @@ -75,5 +75,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.json index 9ca72009d..4b6b720f1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.json @@ -38,6 +38,28 @@ } } + }, + { + "abiName" : "bjs___Swift_Foundation_Greeter_static_makeDefault", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "makeDefault", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + }, + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_Greeter" + } + } } ], "name" : "Greeter", @@ -46,7 +68,21 @@ "Foundation" ], "properties" : [ + { + "isReadonly" : true, + "isStatic" : true, + "name" : "defaultGreeting", + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_Greeter" + } + }, + "type" : { + "string" : { + } + } + } ], "swiftCallName" : "Greeter" }, @@ -253,5 +289,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.swift index 86f0b8478..baa867eed 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.Global.swift @@ -42,6 +42,28 @@ public func _bjs___Swift_Foundation_Greeter_greet(_ _self: UnsafeMutableRawPoint #endif } +@_expose(wasm, "bjs___Swift_Foundation_Greeter_static_makeDefault") +@_cdecl("bjs___Swift_Foundation_Greeter_static_makeDefault") +public func _bjs___Swift_Foundation_Greeter_static_makeDefault() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = Greeter.makeDefault() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs___Swift_Foundation_Greeter_static_defaultGreeting_get") +@_cdecl("bjs___Swift_Foundation_Greeter_static_defaultGreeting_get") +public func _bjs___Swift_Foundation_Greeter_static_defaultGreeting_get() -> Void { + #if arch(wasm32) + let ret = Greeter.defaultGreeting + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs___Swift_Foundation_Greeter_deinit") @_cdecl("bjs___Swift_Foundation_Greeter_deinit") public func _bjs___Swift_Foundation_Greeter_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.json index 0713a2f30..3c07b7dcf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.json @@ -38,6 +38,28 @@ } } + }, + { + "abiName" : "bjs___Swift_Foundation_Greeter_static_makeDefault", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "makeDefault", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + }, + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_Greeter" + } + } } ], "name" : "Greeter", @@ -46,7 +68,21 @@ "Foundation" ], "properties" : [ + { + "isReadonly" : true, + "isStatic" : true, + "name" : "defaultGreeting", + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_Greeter" + } + }, + "type" : { + "string" : { + } + } + } ], "swiftCallName" : "Greeter" }, @@ -253,5 +289,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.swift index 86f0b8478..baa867eed 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Namespaces.swift @@ -42,6 +42,28 @@ public func _bjs___Swift_Foundation_Greeter_greet(_ _self: UnsafeMutableRawPoint #endif } +@_expose(wasm, "bjs___Swift_Foundation_Greeter_static_makeDefault") +@_cdecl("bjs___Swift_Foundation_Greeter_static_makeDefault") +public func _bjs___Swift_Foundation_Greeter_static_makeDefault() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = Greeter.makeDefault() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs___Swift_Foundation_Greeter_static_defaultGreeting_get") +@_cdecl("bjs___Swift_Foundation_Greeter_static_defaultGreeting_get") +public func _bjs___Swift_Foundation_Greeter_static_defaultGreeting_get() -> Void { + #if arch(wasm32) + let ret = Greeter.defaultGreeting + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs___Swift_Foundation_Greeter_deinit") @_cdecl("bjs___Swift_Foundation_Greeter_deinit") public func _bjs___Swift_Foundation_Greeter_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.json new file mode 100644 index 000000000..f924b3eba --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.json @@ -0,0 +1,156 @@ +{ + "exported" : { + "classes" : [ + { + "methods" : [ + { + "abiName" : "bjs_User_getName", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getName", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "User", + "properties" : [ + + ], + "swiftCallName" : "User" + }, + { + "methods" : [ + { + "abiName" : "bjs_Player_getTag", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getTag", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "Player", + "properties" : [ + + ], + "swiftCallName" : "Player" + } + ], + "enums" : [ + + ], + "exposeToGlobal" : false, + "functions" : [ + + ], + "protocols" : [ + + ], + "structs" : [ + { + "methods" : [ + + ], + "name" : "Stats", + "namespace" : [ + "User" + ], + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "health", + "namespace" : [ + "User" + ], + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "score", + "namespace" : [ + "User" + ], + "type" : { + "double" : { + + } + } + } + ], + "swiftCallName" : "User.Stats" + }, + { + "methods" : [ + + ], + "name" : "Stats", + "namespace" : [ + "Player" + ], + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "level", + "namespace" : [ + "Player" + ], + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "rating", + "namespace" : [ + "Player" + ], + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "Player.Stats" + } + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.swift new file mode 100644 index 000000000..ed1a080e9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/NestedType.swift @@ -0,0 +1,179 @@ +extension User.Stats: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> User.Stats { + let score = Double.bridgeJSStackPop() + let health = Int.bridgeJSStackPop() + return User.Stats(health: health, score: score) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.health.bridgeJSStackPush() + self.score.bridgeJSStackPush() + } + + init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_User_Stats(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_User_Stats())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_User_Stats") +fileprivate func _bjs_struct_lower_User_Stats_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_User_Stats_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_User_Stats(_ objectId: Int32) -> Void { + return _bjs_struct_lower_User_Stats_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_User_Stats") +fileprivate func _bjs_struct_lift_User_Stats_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_User_Stats_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_User_Stats() -> Int32 { + return _bjs_struct_lift_User_Stats_extern() +} + +extension Player.Stats: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> Player.Stats { + let rating = String.bridgeJSStackPop() + let level = Int.bridgeJSStackPop() + return Player.Stats(level: level, rating: rating) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.level.bridgeJSStackPush() + self.rating.bridgeJSStackPush() + } + + init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_Player_Stats(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_Player_Stats())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_Player_Stats") +fileprivate func _bjs_struct_lower_Player_Stats_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_Player_Stats_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_Player_Stats(_ objectId: Int32) -> Void { + return _bjs_struct_lower_Player_Stats_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_Player_Stats") +fileprivate func _bjs_struct_lift_Player_Stats_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_Player_Stats_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_Player_Stats() -> Int32 { + return _bjs_struct_lift_Player_Stats_extern() +} + +@_expose(wasm, "bjs_User_getName") +@_cdecl("bjs_User_getName") +public func _bjs_User_getName(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = User.bridgeJSLiftParameter(_self).getName() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_User_deinit") +@_cdecl("bjs_User_deinit") +public func _bjs_User_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension User: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_User_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_User_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_User_wrap") +fileprivate func _bjs_User_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_User_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_User_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_User_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_Player_getTag") +@_cdecl("bjs_Player_getTag") +public func _bjs_Player_getTag(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = Player.bridgeJSLiftParameter(_self).getTag() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Player_deinit") +@_cdecl("bjs_Player_deinit") +public func _bjs_Player_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension Player: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_Player_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_Player_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_Player_wrap") +fileprivate func _bjs_Player_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_Player_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_Player_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_Player_wrap_extern(pointer) +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json index 67d97821c..91291c24e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json @@ -1008,7 +1008,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "valueOrNull", @@ -1040,6 +1042,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "stringOrNull", "type" : { "nullable" : { @@ -1053,6 +1056,7 @@ } }, { + "accessLevel" : "internal", "name" : "stringOrUndefined", "type" : { "nullable" : { @@ -1066,6 +1070,7 @@ } }, { + "accessLevel" : "internal", "name" : "doubleOrNull", "type" : { "nullable" : { @@ -1079,6 +1084,7 @@ } }, { + "accessLevel" : "internal", "name" : "doubleOrUndefined", "type" : { "nullable" : { @@ -1092,6 +1098,7 @@ } }, { + "accessLevel" : "internal", "name" : "boolOrNull", "type" : { "nullable" : { @@ -1105,6 +1112,7 @@ } }, { + "accessLevel" : "internal", "name" : "boolOrUndefined", "type" : { "nullable" : { @@ -1118,6 +1126,7 @@ } }, { + "accessLevel" : "internal", "name" : "intOrNull", "type" : { "nullable" : { @@ -1134,6 +1143,7 @@ } }, { + "accessLevel" : "internal", "name" : "intOrUndefined", "type" : { "nullable" : { @@ -1148,10 +1158,30 @@ "_1" : "undefined" } } + }, + { + "accessLevel" : "internal", + "name" : "childOrNull", + "type" : { + "nullable" : { + "_0" : { + "jsObject" : { + "_0" : "WithOptionalJSClass" + } + }, + "_1" : "null" + } + } } ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripStringOrNull", "parameters" : [ { @@ -1180,6 +1210,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripStringOrUndefined", "parameters" : [ { @@ -1208,6 +1244,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripDoubleOrNull", "parameters" : [ { @@ -1236,6 +1278,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripDoubleOrUndefined", "parameters" : [ { @@ -1264,6 +1312,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripBoolOrNull", "parameters" : [ { @@ -1292,6 +1346,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripBoolOrUndefined", "parameters" : [ { @@ -1320,6 +1380,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripIntOrNull", "parameters" : [ { @@ -1354,6 +1420,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripIntOrUndefined", "parameters" : [ { @@ -1386,11 +1458,46 @@ "_1" : "undefined" } } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "roundTripChildOrNull", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "jsObject" : { + "_0" : "WithOptionalJSClass" + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "jsObject" : { + "_0" : "WithOptionalJSClass" + } + }, + "_1" : "null" + } + } } ], "name" : "WithOptionalJSClass", "setters" : [ { + "accessLevel" : "internal", "functionName" : "stringOrNull_set", "name" : "stringOrNull", "type" : { @@ -1405,6 +1512,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "stringOrUndefined_set", "name" : "stringOrUndefined", "type" : { @@ -1419,6 +1527,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "doubleOrNull_set", "name" : "doubleOrNull", "type" : { @@ -1433,6 +1542,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "doubleOrUndefined_set", "name" : "doubleOrUndefined", "type" : { @@ -1447,6 +1557,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "boolOrNull_set", "name" : "boolOrNull", "type" : { @@ -1461,6 +1572,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "boolOrUndefined_set", "name" : "boolOrUndefined", "type" : { @@ -1475,6 +1587,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "intOrNull_set", "name" : "intOrNull", "type" : { @@ -1492,6 +1605,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "intOrUndefined_set", "name" : "intOrUndefined", "type" : { @@ -1507,6 +1621,21 @@ "_1" : "undefined" } } + }, + { + "accessLevel" : "internal", + "functionName" : "childOrNull_set", + "name" : "childOrNull", + "type" : { + "nullable" : { + "_0" : { + "jsObject" : { + "_0" : "WithOptionalJSClass" + } + }, + "_1" : "null" + } + } } ], "staticMethods" : [ @@ -1517,5 +1646,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift index 0c6a79bf5..0a2528340 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift @@ -526,6 +526,18 @@ fileprivate func bjs_WithOptionalJSClass_intOrUndefined_get_extern(_ self: Int32 return bjs_WithOptionalJSClass_intOrUndefined_get_extern(self) } +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_childOrNull_get") +fileprivate func bjs_WithOptionalJSClass_childOrNull_get_extern(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_childOrNull_get_extern(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WithOptionalJSClass_childOrNull_get(_ self: Int32) -> Void { + return bjs_WithOptionalJSClass_childOrNull_get_extern(self) +} + #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrNull_set") fileprivate func bjs_WithOptionalJSClass_stringOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void @@ -622,6 +634,18 @@ fileprivate func bjs_WithOptionalJSClass_intOrUndefined_set_extern(_ self: Int32 return bjs_WithOptionalJSClass_intOrUndefined_set_extern(self, newValueIsSome, newValueValue) } +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_childOrNull_set") +fileprivate func bjs_WithOptionalJSClass_childOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_childOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WithOptionalJSClass_childOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + return bjs_WithOptionalJSClass_childOrNull_set_extern(self, newValueIsSome, newValueValue) +} + #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripStringOrNull") fileprivate func bjs_WithOptionalJSClass_roundTripStringOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueBytes: Int32, _ valueLength: Int32) -> Void @@ -718,6 +742,18 @@ fileprivate func bjs_WithOptionalJSClass_roundTripIntOrUndefined_extern(_ self: return bjs_WithOptionalJSClass_roundTripIntOrUndefined_extern(self, valueIsSome, valueValue) } +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripChildOrNull") +fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + return bjs_WithOptionalJSClass_roundTripChildOrNull_extern(self, valueIsSome, valueValue) +} + func _$WithOptionalJSClass_init(_ valueOrNull: Optional, _ valueOrUndefined: JSUndefinedOr) throws(JSException) -> JSObject { let ret0 = valueOrNull.bridgeJSWithLoweredParameter { (valueOrNullIsSome, valueOrNullBytes, valueOrNullLength) in let ret1 = valueOrUndefined.bridgeJSWithLoweredParameter { (valueOrUndefinedIsSome, valueOrUndefinedBytes, valueOrUndefinedLength) in @@ -805,6 +841,15 @@ func _$WithOptionalJSClass_intOrUndefined_get(_ self: JSObject) throws(JSExcepti return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() } +func _$WithOptionalJSClass_childOrNull_get(_ self: JSObject) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_childOrNull_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn() +} + func _$WithOptionalJSClass_stringOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { let selfValue = self.bridgeJSLowerParameter() newValue.bridgeJSWithLoweredParameter { (newValueIsSome, newValueBytes, newValueLength) in @@ -879,6 +924,15 @@ func _$WithOptionalJSClass_intOrUndefined_set(_ self: JSObject, _ newValue: JSUn } } +func _$WithOptionalJSClass_childOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_childOrNull_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + func _$WithOptionalJSClass_roundTripStringOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { let selfValue = self.bridgeJSLowerParameter() value.bridgeJSWithLoweredParameter { (valueIsSome, valueBytes, valueLength) in @@ -959,4 +1013,14 @@ func _$WithOptionalJSClass_roundTripIntOrUndefined(_ self: JSObject, _ value: JS throw error } return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripChildOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripChildOrNull(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn() } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json index f75bf7610..320499ff3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json @@ -88,6 +88,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "check", "parameters" : [ { @@ -120,5 +126,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json index cded9a973..414fedbbd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json @@ -112,6 +112,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkNumber", "parameters" : [ @@ -123,6 +129,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkBoolean", "parameters" : [ @@ -140,5 +152,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PropertyTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PropertyTypes.json index 24e3f44cd..281538dd6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PropertyTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PropertyTypes.json @@ -377,5 +377,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json index 757115c59..feca4615b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json @@ -317,6 +317,20 @@ } } } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "delegatesByName", + "type" : { + "dictionary" : { + "_0" : { + "swiftProtocol" : { + "_0" : "MyViewControllerDelegate" + } + } + } + } } ], "swiftCallName" : "DelegateManager" @@ -502,6 +516,39 @@ } } } + }, + { + "abiName" : "bjs_processDelegatesByName", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processDelegatesByName", + "parameters" : [ + { + "label" : "_", + "name" : "delegates", + "type" : { + "dictionary" : { + "_0" : { + "swiftProtocol" : { + "_0" : "MyViewControllerDelegate" + } + } + } + } + } + ], + "returnType" : { + "dictionary" : { + "_0" : { + "swiftProtocol" : { + "_0" : "MyViewControllerDelegate" + } + } + } + } } ], "protocols" : [ @@ -923,5 +970,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift index cf7464cc3..e8df6c966 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift @@ -633,7 +633,7 @@ extension Direction: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -648,7 +648,7 @@ extension Direction: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -698,7 +698,23 @@ public func _bjs_processDelegates() -> Void { #if arch(wasm32) let ret = processDelegates(_: [AnyMyViewControllerDelegate].bridgeJSStackPop()) for __bjs_elem_ret in ret { - _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())} + _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } + _swift_js_push_i32(Int32(ret.count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_processDelegatesByName") +@_cdecl("bjs_processDelegatesByName") +public func _bjs_processDelegatesByName() -> Void { + #if arch(wasm32) + let ret = processDelegatesByName(_: [String: AnyMyViewControllerDelegate].bridgeJSLiftParameter()) + for __bjs_kv_ret in ret { + __bjs_kv_ret.key.bridgeJSStackPush() + _swift_js_push_i32((__bjs_kv_ret.value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } _swift_js_push_i32(Int32(ret.count)) #else fatalError("Only available on WebAssembly") @@ -955,7 +971,8 @@ public func _bjs_DelegateManager_delegates_get(_ _self: UnsafeMutableRawPointer) #if arch(wasm32) let ret = DelegateManager.bridgeJSLiftParameter(_self).delegates for __bjs_elem_ret in ret { - _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())} + _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } _swift_js_push_i32(Int32(ret.count)) #else fatalError("Only available on WebAssembly") @@ -972,6 +989,31 @@ public func _bjs_DelegateManager_delegates_set(_ _self: UnsafeMutableRawPointer) #endif } +@_expose(wasm, "bjs_DelegateManager_delegatesByName_get") +@_cdecl("bjs_DelegateManager_delegatesByName_get") +public func _bjs_DelegateManager_delegatesByName_get(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = DelegateManager.bridgeJSLiftParameter(_self).delegatesByName + for __bjs_kv_ret in ret { + __bjs_kv_ret.key.bridgeJSStackPush() + _swift_js_push_i32((__bjs_kv_ret.value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } + _swift_js_push_i32(Int32(ret.count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_DelegateManager_delegatesByName_set") +@_cdecl("bjs_DelegateManager_delegatesByName_set") +public func _bjs_DelegateManager_delegatesByName_set(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + DelegateManager.bridgeJSLiftParameter(_self).delegatesByName = [String: AnyMyViewControllerDelegate].bridgeJSLiftParameter() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_DelegateManager_deinit") @_cdecl("bjs_DelegateManager_deinit") public func _bjs_DelegateManager_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json index 4ba7ba9a5..70273f8b3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json @@ -84,7 +84,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -130,7 +131,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -166,7 +168,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -191,7 +194,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -232,7 +236,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -277,5 +282,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.json index 7e332fe3a..800018440 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.json @@ -166,8 +166,11 @@ "label" : "a", "name" : "a", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } }, @@ -175,15 +178,21 @@ "label" : "b", "name" : "b", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } ], "returnType" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } }, "staticContext" : { @@ -282,15 +291,21 @@ "label" : "value", "name" : "value", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } ], "returnType" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } }, "staticContext" : { @@ -468,5 +483,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.swift index d329c7a96..896258915 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.Global.swift @@ -12,7 +12,7 @@ extension Calculator: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .scientific @@ -23,7 +23,7 @@ extension Calculator: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .scientific: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.json index 041afc2f9..36110488c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.json @@ -166,8 +166,11 @@ "label" : "a", "name" : "a", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } }, @@ -175,15 +178,21 @@ "label" : "b", "name" : "b", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } ], "returnType" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } }, "staticContext" : { @@ -282,15 +291,21 @@ "label" : "value", "name" : "value", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } ], "returnType" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } }, "staticContext" : { @@ -468,5 +483,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.swift index d329c7a96..896258915 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticFunctions.swift @@ -12,7 +12,7 @@ extension Calculator: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .scientific @@ -23,7 +23,7 @@ extension Calculator: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .scientific: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json index d14f9b0a3..1cbe44619 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json @@ -351,5 +351,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.swift index faec8821b..ded55dbd4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.swift @@ -12,7 +12,7 @@ extension PropertyEnum: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .value1 @@ -23,7 +23,7 @@ extension PropertyEnum: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .value1: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json index 35d740dff..8fc0667b0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json @@ -351,5 +351,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.swift index faec8821b..ded55dbd4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.swift @@ -12,7 +12,7 @@ extension PropertyEnum: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .value1 @@ -23,7 +23,7 @@ extension PropertyEnum: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .value1: return 0 diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json index b0aa8c35b..d9dc0ec43 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json @@ -71,6 +71,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkString", "parameters" : [ { @@ -89,6 +95,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkStringWithLength", "parameters" : [ { @@ -121,5 +133,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json index 3f9271592..e2cf9ffac 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json @@ -38,6 +38,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkString", "parameters" : [ @@ -55,5 +61,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json index cf5156d8d..a3ddab63e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json @@ -121,8 +121,11 @@ "isStatic" : false, "name" : "nameCount", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } }, @@ -210,6 +213,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripGreeter", "parameters" : [ { @@ -228,6 +237,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalGreeter", "parameters" : [ { @@ -262,5 +277,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json index f610d4bde..ac18f6dc2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json @@ -61,7 +61,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -283,6 +284,152 @@ ], "exposeToGlobal" : false, "functions" : [ + { + "abiName" : "bjs_roundtripAnimal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundtripAnimal", + "parameters" : [ + { + "label" : "_", + "name" : "animalClosure", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule6AnimalV_6AnimalV", + "moduleName" : "TestModule", + "parameters" : [ + { + "swiftStruct" : { + "_0" : "Animal" + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + } + ], + "returnType" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule6AnimalV_6AnimalV", + "moduleName" : "TestModule", + "parameters" : [ + { + "swiftStruct" : { + "_0" : "Animal" + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + }, + { + "abiName" : "bjs_roundtripOptionalAnimal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundtripOptionalAnimal", + "parameters" : [ + { + "label" : "_", + "name" : "animalClosure", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModuleSq6AnimalV_Sq6AnimalV", + "moduleName" : "TestModule", + "parameters" : [ + { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "_1" : "null" + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "_1" : "null" + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + } + ], + "returnType" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModuleSq6AnimalV_Sq6AnimalV", + "moduleName" : "TestModule", + "parameters" : [ + { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "_1" : "null" + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Animal" + } + }, + "_1" : "null" + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + }, { "abiName" : "bjs_roundtripString", "effects" : { @@ -313,7 +460,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -338,7 +486,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -380,7 +529,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -411,7 +561,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -447,7 +598,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -472,7 +624,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -508,7 +661,8 @@ "float" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -533,7 +687,8 @@ "float" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -569,7 +724,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -594,7 +750,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -640,7 +797,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -675,7 +833,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -727,7 +886,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -768,7 +928,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -814,7 +975,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -849,7 +1011,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -895,7 +1058,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -930,7 +1094,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -976,7 +1141,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1011,7 +1177,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1047,7 +1214,8 @@ "swiftHeapObject" : { "_0" : "Person" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1072,7 +1240,8 @@ "swiftHeapObject" : { "_0" : "Person" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1118,7 +1287,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1153,7 +1323,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1189,7 +1360,8 @@ "caseEnum" : { "_0" : "Direction" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1214,7 +1386,8 @@ "caseEnum" : { "_0" : "Direction" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1252,7 +1425,8 @@ "_0" : "Theme", "_1" : "String" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1279,7 +1453,8 @@ "_0" : "Theme", "_1" : "String" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1317,7 +1492,8 @@ "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1344,7 +1520,8 @@ "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1380,7 +1557,8 @@ "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1405,7 +1583,8 @@ "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1451,7 +1630,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1486,7 +1666,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1534,7 +1715,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1571,7 +1753,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1619,7 +1802,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1656,7 +1840,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1702,7 +1887,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1737,7 +1923,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1783,7 +1970,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1818,7 +2006,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1829,8 +2018,49 @@ ], "structs" : [ + { + "constructor" : { + "abiName" : "bjs_Animal_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "type", + "name" : "type", + "type" : { + "string" : { + + } + } + } + ] + }, + "explicitAccessControl" : "public", + "methods" : [ + + ], + "name" : "Animal", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "type", + "type" : { + "string" : { + } + } + } + ], + "swiftCallName" : "Animal" + } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift index 472987fee..4eb7c8da4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift @@ -127,6 +127,69 @@ public func _invoke_swift_closure_TestModule_10TestModule5ThemeO_5ThemeO(_ boxPt #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV") +fileprivate func invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV(_ callback: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV") +fileprivate func make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule6AnimalV_6AnimalV { + static func bridgeJSLift(_ callbackId: Int32) -> (Animal) -> Animal { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV(callbackValue) + return Animal.bridgeJSLiftReturn() + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Animal) -> Animal { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Animal) -> Animal) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV") +@_cdecl("invoke_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV") +public func _invoke_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Animal) -> Animal>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Animal.bridgeJSLiftParameter()) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule6PersonC_6PersonC") fileprivate func invoke_js_callback_TestModule_10TestModule6PersonC_6PersonC_extern(_ callback: Int32, _ param0: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer @@ -761,6 +824,69 @@ public func _invoke_swift_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO(_ b #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV") +fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV") +fileprivate func make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuleSq6AnimalV_Sq6AnimalV { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> Optional { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0IsSome = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(callbackValue, param0IsSome) + return Optional.bridgeJSLiftReturn() + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> Optional { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> Optional) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV") +@_cdecl("invoke_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV") +public func _invoke_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter()) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSq6PersonC_Sq6PersonC") fileprivate func invoke_js_callback_TestModule_10TestModuleSq6PersonC_Sq6PersonC_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer @@ -1280,7 +1406,7 @@ extension Direction: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -1295,7 +1421,7 @@ extension Direction: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -1358,6 +1484,85 @@ extension APIResult: _BridgedSwiftAssociatedValueEnum { } } +extension Animal: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> Animal { + let type = String.bridgeJSStackPop() + return Animal(type: type) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.type.bridgeJSStackPush() + } + + public init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_Animal(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + public func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_Animal())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_Animal") +fileprivate func _bjs_struct_lower_Animal_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_Animal_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_Animal(_ objectId: Int32) -> Void { + return _bjs_struct_lower_Animal_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_Animal") +fileprivate func _bjs_struct_lift_Animal_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_Animal_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_Animal() -> Int32 { + return _bjs_struct_lift_Animal_extern() +} + +@_expose(wasm, "bjs_Animal_init") +@_cdecl("bjs_Animal_init") +public func _bjs_Animal_init(_ typeBytes: Int32, _ typeLength: Int32) -> Void { + #if arch(wasm32) + let ret = Animal(type: String.bridgeJSLiftParameter(typeBytes, typeLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundtripAnimal") +@_cdecl("bjs_roundtripAnimal") +public func _bjs_roundtripAnimal(_ animalClosure: Int32) -> Int32 { + #if arch(wasm32) + let ret = roundtripAnimal(_: _BJS_Closure_10TestModule6AnimalV_6AnimalV.bridgeJSLift(animalClosure)) + return JSTypedClosure(ret).bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundtripOptionalAnimal") +@_cdecl("bjs_roundtripOptionalAnimal") +public func _bjs_roundtripOptionalAnimal(_ animalClosure: Int32) -> Int32 { + #if arch(wasm32) + let ret = roundtripOptionalAnimal(_: _BJS_Closure_10TestModuleSq6AnimalV_Sq6AnimalV.bridgeJSLift(animalClosure)) + return JSTypedClosure(ret).bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundtripString") @_cdecl("bjs_roundtripString") public func _bjs_roundtripString(_ stringClosure: Int32) -> Int32 { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json index 4359b50ec..a84441bb4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json @@ -4,6 +4,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "applyInt", "parameters" : [ { @@ -43,7 +49,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -60,6 +67,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeAdder", "parameters" : [ { @@ -98,7 +111,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -111,5 +125,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json index 4c36dc15c..bfde01318 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json @@ -312,8 +312,11 @@ } }, "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } @@ -709,5 +712,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json index c1329cd79..a9b0d22bf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json @@ -56,6 +56,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "translate", "parameters" : [ { @@ -94,6 +100,40 @@ "_0" : "Point" } } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "roundTripOptional", + "parameters" : [ + { + "name" : "point", + "type" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Point" + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Point" + } + }, + "_1" : "null" + } + } } ], "types" : [ @@ -102,5 +142,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.swift index fe79f786c..cec50ffca 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.swift @@ -67,4 +67,25 @@ func _$translate(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException) -> Po throw error } return Point.bridgeJSLiftReturn(ret) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_roundTripOptional") +fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void +#else +fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_roundTripOptional(_ point: Int32) -> Void { + return bjs_roundTripOptional_extern(point) +} + +func _$roundTripOptional(_ point: Optional) throws(JSException) -> Optional { + let pointIsSome = point.bridgeJSLowerParameter() + bjs_roundTripOptional(pointIsSome) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn() } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json new file mode 100644 index 000000000..f6c0c6fe1 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json @@ -0,0 +1,288 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "public", + "getters" : [ + + ], + "jsName" : "PublicEvent", + "methods" : [ + + ], + "name" : "JSPublicEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "package", + "getters" : [ + + ], + "jsName" : "PackageEvent", + "methods" : [ + + ], + "name" : "JSPackageEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "internal", + "getters" : [ + + ], + "jsName" : "InternalEvent", + "methods" : [ + + ], + "name" : "JSInternalEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "public", + "getters" : [ + + ], + "jsName" : "PublicTarget", + "methods" : [ + { + "accessLevel" : "public", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addPublicListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule13JSPublicEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPublicEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addInternalListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule13JSPublicEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPublicEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSPublicTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "package", + "getters" : [ + + ], + "jsName" : "PackageTarget", + "methods" : [ + { + "accessLevel" : "package", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addPackageListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule14JSPackageEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPackageEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSPackageTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "internal", + "getters" : [ + + ], + "jsName" : "InternalTarget", + "methods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addInternalListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule15JSInternalEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSInternalEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSInternalTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + } + ] + } + ] + }, + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift new file mode 100644 index 000000000..fbd181fcc --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift @@ -0,0 +1,266 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule13JSPublicEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSPublicEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSPublicEvent) -> Void { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSPublicEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule13JSPublicEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSPublicEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSPublicEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule14JSPackageEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSPackageEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSPackageEvent) -> Void { + package init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSPackageEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule14JSPackageEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSPackageEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSPackageEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule15JSInternalEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSInternalEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSInternalEvent) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSInternalEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule15JSInternalEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSInternalEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSInternalEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPublicTarget_addPublicListener") +fileprivate func bjs_JSPublicTarget_addPublicListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPublicTarget_addPublicListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPublicTarget_addPublicListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPublicTarget_addPublicListener_extern(self, handler) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPublicTarget_addInternalListener") +fileprivate func bjs_JSPublicTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPublicTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPublicTarget_addInternalListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPublicTarget_addInternalListener_extern(self, handler) +} + +func _$JSPublicTarget_addPublicListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPublicTarget_addPublicListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$JSPublicTarget_addInternalListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPublicTarget_addInternalListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPackageTarget_addPackageListener") +fileprivate func bjs_JSPackageTarget_addPackageListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPackageTarget_addPackageListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPackageTarget_addPackageListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPackageTarget_addPackageListener_extern(self, handler) +} + +func _$JSPackageTarget_addPackageListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPackageEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPackageTarget_addPackageListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSInternalTarget_addInternalListener") +fileprivate func bjs_JSInternalTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSInternalTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSInternalTarget_addInternalListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSInternalTarget_addInternalListener_extern(self, handler) +} + +func _$JSInternalTarget_addInternalListener(_ self: JSObject, _ handler: JSTypedClosure<(JSInternalEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSInternalTarget_addInternalListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Throws.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Throws.json index 02796479f..942e5fb45 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Throws.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Throws.json @@ -33,5 +33,8 @@ ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/UnsafePointer.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/UnsafePointer.json index 1eb9e47ec..a382778e9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/UnsafePointer.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/UnsafePointer.json @@ -412,5 +412,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json index 14da32841..d31f775fb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json @@ -38,6 +38,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "check", "parameters" : [ @@ -55,5 +61,8 @@ } ] }, - "moduleName" : "TestModule" + "moduleName" : "TestModule", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js index 85e9c749a..ad0111929 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js @@ -38,6 +38,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -123,6 +124,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_Point"] = function(objectId) { structHelpers.Point.lower(swift.memory.getObject(objectId)); } @@ -250,12 +258,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_importProcessNumbers"] = function bjs_importProcessNumbers() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const f64 = f64Stack.pop(); - arrayResult.push(f64); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = f64Stack.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); } - arrayResult.reverse(); imports.importProcessNumbers(arrayResult); } catch (error) { setException(error); @@ -275,12 +288,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_importTransformNumbers"] = function bjs_importTransformNumbers() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const f64 = f64Stack.pop(); - arrayResult.push(f64); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = f64Stack.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); } - arrayResult.reverse(); let ret = imports.importTransformNumbers(arrayResult); for (const elem of ret) { f64Stack.push(elem); @@ -293,12 +311,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_importProcessStrings"] = function bjs_importProcessStrings() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const string = strStack.pop(); - arrayResult.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = strStack.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); } - arrayResult.reverse(); let ret = imports.importProcessStrings(arrayResult); for (const elem of ret) { const bytes = textEncoder.encode(elem); @@ -314,12 +337,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_importProcessBooleans"] = function bjs_importProcessBooleans() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const bool = i32Stack.pop() !== 0; - arrayResult.push(bool); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const bool = i32Stack.pop() !== 0; + arrayResult.push(bool); + } + arrayResult.reverse(); } - arrayResult.reverse(); let ret = imports.importProcessBooleans(arrayResult); for (const elem of ret) { i32Stack.push(elem ? 1 : 0); @@ -348,18 +376,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -369,18 +418,19 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Item extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Item_deinit, Item.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Item_deinit, Item.prototype, null); } } class MultiArrayContainer extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MultiArrayContainer_deinit, MultiArrayContainer.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MultiArrayContainer_deinit, MultiArrayContainer.prototype, null); } constructor(nums, strs) { @@ -401,23 +451,33 @@ export async function createInstantiator(options, swift) { get numbers() { instance.exports.bjs_MultiArrayContainer_numbers_get(this.pointer); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; } get strings() { instance.exports.bjs_MultiArrayContainer_strings_get(this.pointer); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const string = strStack.pop(); - arrayResult.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = strStack.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; } } @@ -434,12 +494,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processIntArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processStringArray: function bjs_processStringArray(values) { @@ -452,12 +517,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processStringArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const string = strStack.pop(); - arrayResult.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = strStack.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processDoubleArray: function bjs_processDoubleArray(values) { @@ -467,12 +537,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processDoubleArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const f64 = f64Stack.pop(); - arrayResult.push(f64); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = f64Stack.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processBoolArray: function bjs_processBoolArray(values) { @@ -482,12 +557,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processBoolArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const bool = i32Stack.pop() !== 0; - arrayResult.push(bool); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const bool = i32Stack.pop() !== 0; + arrayResult.push(bool); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processPointArray: function bjs_processPointArray(points) { @@ -497,12 +577,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(points.length); instance.exports.bjs_processPointArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const struct = structHelpers.Point.lift(); - arrayResult.push(struct); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const struct = structHelpers.Point.lift(); + arrayResult.push(struct); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processDirectionArray: function bjs_processDirectionArray(directions) { @@ -512,12 +597,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(directions.length); instance.exports.bjs_processDirectionArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const caseId = i32Stack.pop(); - arrayResult.push(caseId); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const caseId = i32Stack.pop(); + arrayResult.push(caseId); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processStatusArray: function bjs_processStatusArray(statuses) { @@ -527,12 +617,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(statuses.length); instance.exports.bjs_processStatusArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const rawValue = i32Stack.pop(); - arrayResult.push(rawValue); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const rawValue = i32Stack.pop(); + arrayResult.push(rawValue); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, sumIntArray: function bjs_sumIntArray(values) { @@ -561,12 +656,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processUnsafeRawPointerArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const pointer = ptrStack.pop(); - arrayResult.push(pointer); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const pointer = ptrStack.pop(); + arrayResult.push(pointer); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processUnsafeMutableRawPointerArray: function bjs_processUnsafeMutableRawPointerArray(values) { @@ -576,12 +676,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processUnsafeMutableRawPointerArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const pointer = ptrStack.pop(); - arrayResult.push(pointer); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const pointer = ptrStack.pop(); + arrayResult.push(pointer); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOpaquePointerArray: function bjs_processOpaquePointerArray(values) { @@ -591,12 +696,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processOpaquePointerArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const pointer = ptrStack.pop(); - arrayResult.push(pointer); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const pointer = ptrStack.pop(); + arrayResult.push(pointer); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalIntArray: function bjs_processOptionalIntArray(values) { @@ -610,19 +720,24 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processOptionalIntArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const int = i32Stack.pop(); - optValue = int; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const int = i32Stack.pop(); + optValue = int; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalStringArray: function bjs_processOptionalStringArray(values) { @@ -639,19 +754,24 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processOptionalStringArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const string = strStack.pop(); - optValue = string; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const string = strStack.pop(); + optValue = string; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalArray: function bjs_processOptionalArray(values) { @@ -668,12 +788,17 @@ export async function createInstantiator(options, swift) { let optResult; if (isSome1) { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); optResult = arrayResult; } else { optResult = null; @@ -691,19 +816,24 @@ export async function createInstantiator(options, swift) { i32Stack.push(points.length); instance.exports.bjs_processOptionalPointArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const struct = structHelpers.Point.lift(); - optValue = struct; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const struct = structHelpers.Point.lift(); + optValue = struct; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalDirectionArray: function bjs_processOptionalDirectionArray(directions) { @@ -717,19 +847,24 @@ export async function createInstantiator(options, swift) { i32Stack.push(directions.length); instance.exports.bjs_processOptionalDirectionArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const caseId = i32Stack.pop(); - optValue = caseId; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const caseId = i32Stack.pop(); + optValue = caseId; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalStatusArray: function bjs_processOptionalStatusArray(statuses) { @@ -743,19 +878,24 @@ export async function createInstantiator(options, swift) { i32Stack.push(statuses.length); instance.exports.bjs_processOptionalStatusArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const rawValue = i32Stack.pop(); - optValue = rawValue; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const rawValue = i32Stack.pop(); + optValue = rawValue; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processNestedIntArray: function bjs_processNestedIntArray(values) { @@ -768,18 +908,28 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processNestedIntArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const arrayLen1 = i32Stack.pop(); - const arrayResult1 = []; - for (let i1 = 0; i1 < arrayLen1; i1++) { - const int = i32Stack.pop(); - arrayResult1.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const arrayLen1 = i32Stack.pop(); + let arrayResult1; + if (arrayLen1 === -1) { + arrayResult1 = taStack.pop(); + } else { + arrayResult1 = []; + for (let i1 = 0; i1 < arrayLen1; i1++) { + const int = i32Stack.pop(); + arrayResult1.push(int); + } + arrayResult1.reverse(); + } + arrayResult.push(arrayResult1); } - arrayResult1.reverse(); - arrayResult.push(arrayResult1); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processNestedStringArray: function bjs_processNestedStringArray(values) { @@ -795,18 +945,28 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_processNestedStringArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const arrayLen1 = i32Stack.pop(); - const arrayResult1 = []; - for (let i1 = 0; i1 < arrayLen1; i1++) { - const string = strStack.pop(); - arrayResult1.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const arrayLen1 = i32Stack.pop(); + let arrayResult1; + if (arrayLen1 === -1) { + arrayResult1 = taStack.pop(); + } else { + arrayResult1 = []; + for (let i1 = 0; i1 < arrayLen1; i1++) { + const string = strStack.pop(); + arrayResult1.push(string); + } + arrayResult1.reverse(); + } + arrayResult.push(arrayResult1); } - arrayResult1.reverse(); - arrayResult.push(arrayResult1); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processNestedPointArray: function bjs_processNestedPointArray(points) { @@ -819,18 +979,28 @@ export async function createInstantiator(options, swift) { i32Stack.push(points.length); instance.exports.bjs_processNestedPointArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const arrayLen1 = i32Stack.pop(); - const arrayResult1 = []; - for (let i1 = 0; i1 < arrayLen1; i1++) { - const struct = structHelpers.Point.lift(); - arrayResult1.push(struct); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const arrayLen1 = i32Stack.pop(); + let arrayResult1; + if (arrayLen1 === -1) { + arrayResult1 = taStack.pop(); + } else { + arrayResult1 = []; + for (let i1 = 0; i1 < arrayLen1; i1++) { + const struct = structHelpers.Point.lift(); + arrayResult1.push(struct); + } + arrayResult1.reverse(); + } + arrayResult.push(arrayResult1); } - arrayResult1.reverse(); - arrayResult.push(arrayResult1); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processItemArray: function bjs_processItemArray(items) { @@ -840,13 +1010,18 @@ export async function createInstantiator(options, swift) { i32Stack.push(items.length); instance.exports.bjs_processItemArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const ptr = ptrStack.pop(); - const obj = Item.__construct(ptr); - arrayResult.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const ptr = ptrStack.pop(); + const obj = Item.__construct(ptr); + arrayResult.push(obj); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processNestedItemArray: function bjs_processNestedItemArray(items) { @@ -859,19 +1034,29 @@ export async function createInstantiator(options, swift) { i32Stack.push(items.length); instance.exports.bjs_processNestedItemArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const arrayLen1 = i32Stack.pop(); - const arrayResult1 = []; - for (let i1 = 0; i1 < arrayLen1; i1++) { - const ptr = ptrStack.pop(); - const obj = Item.__construct(ptr); - arrayResult1.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const arrayLen1 = i32Stack.pop(); + let arrayResult1; + if (arrayLen1 === -1) { + arrayResult1 = taStack.pop(); + } else { + arrayResult1 = []; + for (let i1 = 0; i1 < arrayLen1; i1++) { + const ptr = ptrStack.pop(); + const obj = Item.__construct(ptr); + arrayResult1.push(obj); + } + arrayResult1.reverse(); + } + arrayResult.push(arrayResult1); } - arrayResult1.reverse(); - arrayResult.push(arrayResult1); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processJSObjectArray: function bjs_processJSObjectArray(objects) { @@ -882,14 +1067,19 @@ export async function createInstantiator(options, swift) { i32Stack.push(objects.length); instance.exports.bjs_processJSObjectArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const objId1 = i32Stack.pop(); - const obj = swift.memory.getObject(objId1); - swift.memory.release(objId1); - arrayResult.push(obj); - } - arrayResult.reverse(); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + arrayResult.push(obj); + } + arrayResult.reverse(); + } return arrayResult; }, processOptionalJSObjectArray: function bjs_processOptionalJSObjectArray(objects) { @@ -904,21 +1094,26 @@ export async function createInstantiator(options, swift) { i32Stack.push(objects.length); instance.exports.bjs_processOptionalJSObjectArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const objId1 = i32Stack.pop(); - const obj = swift.memory.getObject(objId1); - swift.memory.release(objId1); - optValue = obj; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + optValue = obj; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processNestedJSObjectArray: function bjs_processNestedJSObjectArray(objects) { @@ -932,20 +1127,30 @@ export async function createInstantiator(options, swift) { i32Stack.push(objects.length); instance.exports.bjs_processNestedJSObjectArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const arrayLen1 = i32Stack.pop(); - const arrayResult1 = []; - for (let i1 = 0; i1 < arrayLen1; i1++) { - const objId1 = i32Stack.pop(); - const obj = swift.memory.getObject(objId1); - swift.memory.release(objId1); - arrayResult1.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const arrayLen1 = i32Stack.pop(); + let arrayResult1; + if (arrayLen1 === -1) { + arrayResult1 = taStack.pop(); + } else { + arrayResult1 = []; + for (let i1 = 0; i1 < arrayLen1; i1++) { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + arrayResult1.push(obj); + } + arrayResult1.reverse(); + } + arrayResult.push(arrayResult1); } - arrayResult1.reverse(); - arrayResult.push(arrayResult1); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, multiArrayParams: function bjs_multiArrayParams(nums, strs) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js index bf368738e..a4c42674e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts new file mode 100644 index 000000000..e612ae1e1 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts @@ -0,0 +1,23 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export type Exports = { +} +export type Imports = { + asyncReturnVoid(): Promise; + asyncRoundTripInt(v: number): Promise; + asyncRoundTripString(v: string): Promise; + asyncRoundTripBool(v: boolean): Promise; + asyncRoundTripDouble(v: number): Promise; + asyncRoundTripJSObject(v: any): Promise; +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js new file mode 100644 index 000000000..fd27e3d67 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -0,0 +1,515 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + function __bjs_jsValueLower(value) { + let kind; + let payload1; + let payload2; + if (value === null) { + kind = 4; + payload1 = 0; + payload2 = 0; + } else { + switch (typeof value) { + case "boolean": + kind = 0; + payload1 = value ? 1 : 0; + payload2 = 0; + break; + case "number": + kind = 2; + payload1 = 0; + payload2 = value; + break; + case "string": + kind = 1; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "undefined": + kind = 5; + payload1 = 0; + payload2 = 0; + break; + case "object": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "function": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "symbol": + kind = 7; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "bigint": + kind = 8; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + default: + throw new TypeError("Unsupported JSValue type"); + } + } + return [kind, payload1, payload2]; + } + function __bjs_jsValueLift(kind, payload1, payload2) { + let jsValue; + switch (kind) { + case 0: + jsValue = payload1 !== 0; + break; + case 1: + jsValue = swift.memory.getObject(payload1); + break; + case 2: + jsValue = payload2; + break; + case 3: + jsValue = swift.memory.getObject(payload1); + break; + case 4: + jsValue = null; + break; + case 5: + jsValue = undefined; + break; + case 7: + jsValue = swift.memory.getObject(payload1); + break; + case 8: + jsValue = swift.memory.getObject(payload1); + break; + default: + throw new TypeError("Unsupported JSValue kind " + kind); + } + return jsValue; + } + + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModules7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { + try { + const callback = swift.memory.getObject(callbackId); + const jsValue = __bjs_jsValueLift(param0Kind, param0Payload1, param0Payload2); + callback(jsValue); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModules7JSValueV_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules7JSValueV_y = function(param0) { + const [param0Kind, param0Payload1, param0Payload2] = __bjs_jsValueLower(param0); + instance.exports.invoke_swift_closure_TestModule_10TestModules7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules7JSValueV_y); + } + bjs["invoke_js_callback_TestModule_10TestModules8JSObjectC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModules8JSObjectC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules8JSObjectC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModules8JSObjectC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules8JSObjectC_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSS_y"] = function(callbackId, param0Bytes, param0Count) { + try { + const callback = swift.memory.getObject(callbackId); + const string = decodeString(param0Bytes, param0Count); + callback(string); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSS_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSS_y = function(param0) { + const param0Bytes = textEncoder.encode(param0); + const param0Id = swift.memory.retain(param0Bytes); + instance.exports.invoke_swift_closure_TestModule_10TestModulesSS_y(boxPtr, param0Id, param0Bytes.length); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSS_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSb_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0 !== 0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSb_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSb_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSb_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSb_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSd_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSd_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSd_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSd_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSd_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSi_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSi_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSi_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSi_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSi_y); + } + bjs["invoke_js_callback_TestModule_10TestModuley_y"] = function(callbackId) { + try { + const callback = swift.memory.getObject(callbackId); + callback(); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuley_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuley_y = function() { + instance.exports.invoke_swift_closure_TestModule_10TestModuley_y(boxPtr); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuley_y); + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(resolveRef, rejectRef) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.asyncReturnVoid().then(resolve, reject); + } + TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.asyncRoundTripInt(v).then(resolve, reject); + } + TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vCount) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + const string = decodeString(vBytes, vCount); + imports.asyncRoundTripString(string).then(resolve, reject); + } + TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.asyncRoundTripBool(v !== 0).then(resolve, reject); + } + TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.asyncRoundTripDouble(v).then(resolve, reject); + } + TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.asyncRoundTripJSObject(swift.memory.getObject(v)).then(resolve, reject); + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts new file mode 100644 index 000000000..491a66795 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts @@ -0,0 +1,23 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export interface AsyncBox { +} +export type Exports = { +} +export type Imports = { + AsyncBox: { + asyncStaticRoundTrip(v: number): Promise; + asyncStaticVoid(): Promise; + } +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js new file mode 100644 index 000000000..6b6698377 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js @@ -0,0 +1,410 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + function __bjs_jsValueLower(value) { + let kind; + let payload1; + let payload2; + if (value === null) { + kind = 4; + payload1 = 0; + payload2 = 0; + } else { + switch (typeof value) { + case "boolean": + kind = 0; + payload1 = value ? 1 : 0; + payload2 = 0; + break; + case "number": + kind = 2; + payload1 = 0; + payload2 = value; + break; + case "string": + kind = 1; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "undefined": + kind = 5; + payload1 = 0; + payload2 = 0; + break; + case "object": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "function": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "symbol": + kind = 7; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "bigint": + kind = 8; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + default: + throw new TypeError("Unsupported JSValue type"); + } + } + return [kind, payload1, payload2]; + } + function __bjs_jsValueLift(kind, payload1, payload2) { + let jsValue; + switch (kind) { + case 0: + jsValue = payload1 !== 0; + break; + case 1: + jsValue = swift.memory.getObject(payload1); + break; + case 2: + jsValue = payload2; + break; + case 3: + jsValue = swift.memory.getObject(payload1); + break; + case 4: + jsValue = null; + break; + case 5: + jsValue = undefined; + break; + case 7: + jsValue = swift.memory.getObject(payload1); + break; + case 8: + jsValue = swift.memory.getObject(payload1); + break; + default: + throw new TypeError("Unsupported JSValue kind " + kind); + } + return jsValue; + } + + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModules7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { + try { + const callback = swift.memory.getObject(callbackId); + const jsValue = __bjs_jsValueLift(param0Kind, param0Payload1, param0Payload2); + callback(jsValue); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModules7JSValueV_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules7JSValueV_y = function(param0) { + const [param0Kind, param0Payload1, param0Payload2] = __bjs_jsValueLower(param0); + instance.exports.invoke_swift_closure_TestModule_10TestModules7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules7JSValueV_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSd_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSd_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSd_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSd_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSd_y); + } + bjs["invoke_js_callback_TestModule_10TestModuley_y"] = function(callbackId) { + try { + const callback = swift.memory.getObject(callbackId); + callback(); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuley_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuley_y = function() { + instance.exports.invoke_swift_closure_TestModule_10TestModuley_y(boxPtr); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuley_y); + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_AsyncBox_asyncStaticRoundTrip_static"] = function bjs_AsyncBox_asyncStaticRoundTrip_static(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.AsyncBox.asyncStaticRoundTrip(v).then(resolve, reject); + } + TestModule["bjs_AsyncBox_asyncStaticVoid_static"] = function bjs_AsyncBox_asyncStaticVoid_static(resolveRef, rejectRef) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.AsyncBox.asyncStaticVoid().then(resolve, reject); + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js index 004320e4b..8f8463bf0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js @@ -31,6 +31,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -140,6 +141,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_Config"] = function(objectId) { structHelpers.Config.lower(swift.memory.getObject(objectId)); } @@ -279,18 +287,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -300,12 +329,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class DefaultGreeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DefaultGreeter_deinit, DefaultGreeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DefaultGreeter_deinit, DefaultGreeter.prototype, null); } constructor(name) { @@ -328,7 +358,7 @@ export async function createInstantiator(options, swift) { } class EmptyGreeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_EmptyGreeter_deinit, EmptyGreeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_EmptyGreeter_deinit, EmptyGreeter.prototype, null); } constructor() { @@ -338,7 +368,7 @@ export async function createInstantiator(options, swift) { } class ConstructorDefaults extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ConstructorDefaults_deinit, ConstructorDefaults.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ConstructorDefaults_deinit, ConstructorDefaults.prototype, null); } constructor(name = "Default", count = 42, enabled = true, status = StatusValues.Active, tag = null) { @@ -528,12 +558,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_testIntArrayDefault(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, testStringArrayDefault: function bjs_testStringArrayDefault(names = ["a", "b", "c"]) { @@ -546,12 +581,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(names.length); instance.exports.bjs_testStringArrayDefault(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const string = strStack.pop(); - arrayResult.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = strStack.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, testDoubleArrayDefault: function bjs_testDoubleArrayDefault(values = [1.5, 2.5, 3.5]) { @@ -561,12 +601,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_testDoubleArrayDefault(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const f64 = f64Stack.pop(); - arrayResult.push(f64); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = f64Stack.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, testBoolArrayDefault: function bjs_testBoolArrayDefault(flags = [true, false, true]) { @@ -576,12 +621,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(flags.length); instance.exports.bjs_testBoolArrayDefault(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const bool = i32Stack.pop() !== 0; - arrayResult.push(bool); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const bool = i32Stack.pop() !== 0; + arrayResult.push(bool); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, testEmptyArrayDefault: function bjs_testEmptyArrayDefault(items = []) { @@ -591,12 +641,17 @@ export async function createInstantiator(options, swift) { i32Stack.push(items.length); instance.exports.bjs_testEmptyArrayDefault(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, testMixedWithArrayDefault: function bjs_testMixedWithArrayDefault(name = "test", values = [10, 20, 30], enabled = true) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.d.ts index dadcc74ba..f14b29aa4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.d.ts @@ -4,6 +4,10 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. +export interface Counters { + name: string; + counts: Record; +} /// Represents a Swift heap object like a class instance or an actor instance. export interface SwiftHeapObject { /// Release the heap object. @@ -21,6 +25,7 @@ export type Exports = { nestedDictionary(values: Record): Record; boxDictionary(boxes: Record): Record; optionalBoxDictionary(boxes: Record): Record; + roundtripCounters(counters: Counters): Counters; } export type Imports = { importMirrorDictionary(values: Record): Record; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js index 1e9059e34..d040df41c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js @@ -25,11 +25,52 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; let _exports = null; let bjs = null; + const __bjs_createCountersHelpers = () => ({ + lower: (value) => { + const bytes = textEncoder.encode(value.name); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const entries = Object.entries(value.counts); + for (const entry of entries) { + const [key, value] = entry; + const bytes1 = textEncoder.encode(key); + const id1 = swift.memory.retain(bytes1); + i32Stack.push(bytes1.length); + i32Stack.push(id1); + const isSome = value != null ? 1 : 0; + if (isSome) { + i32Stack.push((value | 0)); + } + i32Stack.push(isSome); + } + i32Stack.push(entries.length); + }, + lift: () => { + const dictLen = i32Stack.pop(); + const dictResult = {}; + for (let i = 0; i < dictLen; i++) { + const isSome = i32Stack.pop(); + let optValue; + if (isSome === 0) { + optValue = null; + } else { + const int = i32Stack.pop(); + optValue = int; + } + const string = strStack.pop(); + dictResult[string] = optValue; + } + const string1 = strStack.pop(); + return { name: string1, counts: dictResult }; + } + }); return { /** @@ -99,6 +140,20 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_struct_lower_Counters"] = function(objectId) { + structHelpers.Counters.lower(swift.memory.getObject(objectId)); + } + bjs["swift_js_struct_lift_Counters"] = function() { + const value = structHelpers.Counters.lift(); + return swift.memory.retain(value); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -241,18 +296,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -262,15 +338,19 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Box extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Box_deinit, Box.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Box_deinit, Box.prototype, null); } } + const CountersHelpers = __bjs_createCountersHelpers(); + structHelpers.Counters = CountersHelpers; + const exports = { Box, mirrorDictionary: function bjs_mirrorDictionary(values) { @@ -348,12 +428,17 @@ export async function createInstantiator(options, swift) { const dictResult = {}; for (let i = 0; i < dictLen; i++) { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i1 = 0; i1 < arrayLen; i1++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i1 = 0; i1 < arrayLen; i1++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); const string = strStack.pop(); dictResult[string] = arrayResult; } @@ -414,6 +499,12 @@ export async function createInstantiator(options, swift) { } return dictResult; }, + roundtripCounters: function bjs_roundtripCounters(counters) { + structHelpers.Counters.lower(counters); + instance.exports.bjs_roundtripCounters(); + const structValue = structHelpers.Counters.lift(); + return structValue; + }, }; _exports = exports; return exports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js index eb474d3b0..23819a6e8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js @@ -106,6 +106,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -623,12 +624,17 @@ export async function createInstantiator(options, swift) { } case AllTypesResultValues.Tag.ArrayPayload: { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); return { tag: AllTypesResultValues.Tag.ArrayPayload, param0: arrayResult }; } case AllTypesResultValues.Tag.Empty: return { tag: AllTypesResultValues.Tag.Empty }; @@ -748,12 +754,17 @@ export async function createInstantiator(options, swift) { optValue = null; } else { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); optValue = arrayResult; } return { tag: OptionalAllTypesResultValues.Tag.OptArray, param0: optValue }; @@ -831,6 +842,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_Point"] = function(objectId) { structHelpers.Point.lower(swift.memory.getObject(objectId)); } @@ -955,18 +973,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -976,12 +1015,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class User extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_User_deinit, User.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_User_deinit, User.prototype, null); } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js index fe94c046f..5272717ec 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js @@ -49,6 +49,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -122,6 +123,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.d.ts new file mode 100644 index 000000000..fe48c9174 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.d.ts @@ -0,0 +1,33 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export const SignalValues: { + readonly Start: 0; + readonly Stop: 1; +}; +export type SignalTag = typeof SignalValues[keyof typeof SignalValues]; + +export type SignalObject = typeof SignalValues; + +export interface SignalControls { + send(signal: SignalTag): void; + current(): SignalTag; +} +export type Exports = { + Signal: SignalObject +} +export type Imports = { + SignalControls: { + roundTrip(signal: SignalTag): SignalTag; + } +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.js new file mode 100644 index 000000000..e232c7cbb --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCaseImport.js @@ -0,0 +1,251 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export const SignalValues = { + Start: 0, + Stop: 1, +}; + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_SignalControls_roundTrip_static"] = function bjs_SignalControls_roundTrip_static(signal) { + try { + let ret = imports.SignalControls.roundTrip(signal); + return ret; + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_SignalControls_send"] = function bjs_SignalControls_send(self, signal) { + try { + swift.memory.getObject(self).send(signal); + } catch (error) { + setException(error); + } + } + TestModule["bjs_SignalControls_current"] = function bjs_SignalControls_current(self) { + try { + let ret = swift.memory.getObject(self).current(); + return ret; + } catch (error) { + setException(error); + return 0 + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + Signal: SignalValues, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js index 948039cf9..ecf121aa4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js @@ -69,6 +69,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -142,6 +143,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -271,18 +279,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -292,12 +321,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converter_deinit, Converter.prototype, null); } constructor() { @@ -320,7 +350,7 @@ export async function createInstantiator(options, swift) { } class HTTPServer extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_API_HTTPServer_deinit, HTTPServer.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_API_HTTPServer_deinit, HTTPServer.prototype, null); } constructor() { @@ -333,7 +363,7 @@ export async function createInstantiator(options, swift) { } class TestServer extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_APIV2_Internal_TestServer_deinit, TestServer.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_APIV2_Internal_TestServer_deinit, TestServer.prototype, null); } constructor() { @@ -346,7 +376,7 @@ export async function createInstantiator(options, swift) { } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Formatting_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Formatting_Converter_deinit, Converter.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js index 5201350b2..247a11e54 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js @@ -50,6 +50,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -123,6 +124,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -252,18 +260,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -273,12 +302,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converter_deinit, Converter.prototype, null); } constructor() { @@ -301,7 +331,7 @@ export async function createInstantiator(options, swift) { } class HTTPServer extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_API_HTTPServer_deinit, HTTPServer.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_API_HTTPServer_deinit, HTTPServer.prototype, null); } constructor() { @@ -314,7 +344,7 @@ export async function createInstantiator(options, swift) { } class TestServer extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_APIV2_Internal_TestServer_deinit, TestServer.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Networking_APIV2_Internal_TestServer_deinit, TestServer.prototype, null); } constructor() { @@ -327,7 +357,7 @@ export async function createInstantiator(options, swift) { } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Formatting_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Formatting_Converter_deinit, Converter.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js index 7e5334811..4e4449e06 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js @@ -100,6 +100,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -174,6 +175,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -432,7 +440,7 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalFileSize: function bjs_roundTripOptionalFileSize(input) { const isSome = input != null; - instance.exports.bjs_roundTripOptionalFileSize(+isSome, isSome ? input : 0); + instance.exports.bjs_roundTripOptionalFileSize(+isSome, isSome ? input : 0n); const isSome1 = i32Stack.pop(); let optResult; if (isSome1) { @@ -480,7 +488,7 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalSessionId: function bjs_roundTripOptionalSessionId(input) { const isSome = input != null; - instance.exports.bjs_roundTripOptionalSessionId(+isSome, isSome ? input : 0); + instance.exports.bjs_roundTripOptionalSessionId(+isSome, isSome ? input : 0n); const isSome1 = i32Stack.pop(); let optResult; if (isSome1) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/FixedWidthIntegers.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/FixedWidthIntegers.js index 53ddd7301..211cbefa3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/FixedWidthIntegers.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/FixedWidthIntegers.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -250,7 +258,7 @@ export async function createInstantiator(options, swift) { return ret; } catch (error) { setException(error); - return 0 + return 0n } } TestModule["bjs_roundTripUInt64"] = function bjs_roundTripUInt64(v) { @@ -259,7 +267,7 @@ export async function createInstantiator(options, swift) { return ret; } catch (error) { setException(error); - return 0 + return 0n } } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js index 346b74eac..f5895589d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js index f74095374..77e8002f8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.d.ts new file mode 100644 index 000000000..e5e2a3a84 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.d.ts @@ -0,0 +1,42 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface CachedModel extends SwiftHeapObject { + name: string; +} +export interface UncachedModel extends SwiftHeapObject { + value: number; +} +export interface ExplicitlyUncachedModel extends SwiftHeapObject { + count: number; +} +export type Exports = { + CachedModel: { + new(name: string): CachedModel; + } + UncachedModel: { + new(value: number): UncachedModel; + } + ExplicitlyUncachedModel: { + new(count: number): ExplicitlyUncachedModel; + } +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.js new file mode 100644 index 000000000..db876ff02 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.ConfigPointer.js @@ -0,0 +1,350 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_CachedModel_wrap"] = function(pointer) { + const obj = _exports['CachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_ExplicitlyUncachedModel_wrap"] = function(pointer) { + const obj = _exports['ExplicitlyUncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_UncachedModel_wrap"] = function(pointer) { + const obj = _exports['UncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.hasReleased) { + return; + } + state.hasReleased = true; + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + }); + + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); + } + + release() { + const state = this.__swiftHeapObjectState; + if (state.hasReleased) { + return; + } + state.hasReleased = true; + swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + } + } + class CachedModel extends SwiftHeapObject { + static __identityCache = new Map(); + + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_CachedModel_deinit, CachedModel.prototype, CachedModel.__identityCache); + } + + constructor(name) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const ret = instance.exports.bjs_CachedModel_init(nameId, nameBytes.length); + return CachedModel.__construct(ret); + } + get name() { + instance.exports.bjs_CachedModel_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_CachedModel_name_set(this.pointer, valueId, valueBytes.length); + } + } + class UncachedModel extends SwiftHeapObject { + static __identityCache = new Map(); + + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_UncachedModel_deinit, UncachedModel.prototype, UncachedModel.__identityCache); + } + + constructor(value) { + const ret = instance.exports.bjs_UncachedModel_init(value); + return UncachedModel.__construct(ret); + } + get value() { + const ret = instance.exports.bjs_UncachedModel_value_get(this.pointer); + return ret; + } + set value(value) { + instance.exports.bjs_UncachedModel_value_set(this.pointer, value); + } + } + class ExplicitlyUncachedModel extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ExplicitlyUncachedModel_deinit, ExplicitlyUncachedModel.prototype, null); + } + + constructor(count) { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_init(count); + return ExplicitlyUncachedModel.__construct(ret); + } + get count() { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_count_get(this.pointer); + return ret; + } + set count(value) { + instance.exports.bjs_ExplicitlyUncachedModel_count_set(this.pointer, value); + } + } + const exports = { + CachedModel, + UncachedModel, + ExplicitlyUncachedModel, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.d.ts new file mode 100644 index 000000000..e5e2a3a84 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.d.ts @@ -0,0 +1,42 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface CachedModel extends SwiftHeapObject { + name: string; +} +export interface UncachedModel extends SwiftHeapObject { + value: number; +} +export interface ExplicitlyUncachedModel extends SwiftHeapObject { + count: number; +} +export type Exports = { + CachedModel: { + new(name: string): CachedModel; + } + UncachedModel: { + new(value: number): UncachedModel; + } + ExplicitlyUncachedModel: { + new(count: number): ExplicitlyUncachedModel; + } +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.js new file mode 100644 index 000000000..ca958e564 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.PerClass.js @@ -0,0 +1,348 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_CachedModel_wrap"] = function(pointer) { + const obj = _exports['CachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_ExplicitlyUncachedModel_wrap"] = function(pointer) { + const obj = _exports['ExplicitlyUncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_UncachedModel_wrap"] = function(pointer) { + const obj = _exports['UncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.hasReleased) { + return; + } + state.hasReleased = true; + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + }); + + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); + } + + release() { + const state = this.__swiftHeapObjectState; + if (state.hasReleased) { + return; + } + state.hasReleased = true; + swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + } + } + class CachedModel extends SwiftHeapObject { + static __identityCache = new Map(); + + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_CachedModel_deinit, CachedModel.prototype, CachedModel.__identityCache); + } + + constructor(name) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const ret = instance.exports.bjs_CachedModel_init(nameId, nameBytes.length); + return CachedModel.__construct(ret); + } + get name() { + instance.exports.bjs_CachedModel_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_CachedModel_name_set(this.pointer, valueId, valueBytes.length); + } + } + class UncachedModel extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_UncachedModel_deinit, UncachedModel.prototype, null); + } + + constructor(value) { + const ret = instance.exports.bjs_UncachedModel_init(value); + return UncachedModel.__construct(ret); + } + get value() { + const ret = instance.exports.bjs_UncachedModel_value_get(this.pointer); + return ret; + } + set value(value) { + instance.exports.bjs_UncachedModel_value_set(this.pointer, value); + } + } + class ExplicitlyUncachedModel extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ExplicitlyUncachedModel_deinit, ExplicitlyUncachedModel.prototype, null); + } + + constructor(count) { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_init(count); + return ExplicitlyUncachedModel.__construct(ret); + } + get count() { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_count_get(this.pointer); + return ret; + } + set count(value) { + instance.exports.bjs_ExplicitlyUncachedModel_count_set(this.pointer, value); + } + } + const exports = { + CachedModel, + UncachedModel, + ExplicitlyUncachedModel, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.d.ts new file mode 100644 index 000000000..e5e2a3a84 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.d.ts @@ -0,0 +1,42 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface CachedModel extends SwiftHeapObject { + name: string; +} +export interface UncachedModel extends SwiftHeapObject { + value: number; +} +export interface ExplicitlyUncachedModel extends SwiftHeapObject { + count: number; +} +export type Exports = { + CachedModel: { + new(name: string): CachedModel; + } + UncachedModel: { + new(value: number): UncachedModel; + } + ExplicitlyUncachedModel: { + new(count: number): ExplicitlyUncachedModel; + } +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.js new file mode 100644 index 000000000..ca958e564 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/IdentityModeClass.js @@ -0,0 +1,348 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_CachedModel_wrap"] = function(pointer) { + const obj = _exports['CachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_ExplicitlyUncachedModel_wrap"] = function(pointer) { + const obj = _exports['ExplicitlyUncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_UncachedModel_wrap"] = function(pointer) { + const obj = _exports['UncachedModel'].__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.hasReleased) { + return; + } + state.hasReleased = true; + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + }); + + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); + } + + release() { + const state = this.__swiftHeapObjectState; + if (state.hasReleased) { + return; + } + state.hasReleased = true; + swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + } + } + class CachedModel extends SwiftHeapObject { + static __identityCache = new Map(); + + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_CachedModel_deinit, CachedModel.prototype, CachedModel.__identityCache); + } + + constructor(name) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const ret = instance.exports.bjs_CachedModel_init(nameId, nameBytes.length); + return CachedModel.__construct(ret); + } + get name() { + instance.exports.bjs_CachedModel_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_CachedModel_name_set(this.pointer, valueId, valueBytes.length); + } + } + class UncachedModel extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_UncachedModel_deinit, UncachedModel.prototype, null); + } + + constructor(value) { + const ret = instance.exports.bjs_UncachedModel_init(value); + return UncachedModel.__construct(ret); + } + get value() { + const ret = instance.exports.bjs_UncachedModel_value_get(this.pointer); + return ret; + } + set value(value) { + instance.exports.bjs_UncachedModel_value_set(this.pointer, value); + } + } + class ExplicitlyUncachedModel extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ExplicitlyUncachedModel_deinit, ExplicitlyUncachedModel.prototype, null); + } + + constructor(count) { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_init(count); + return ExplicitlyUncachedModel.__construct(ret); + } + get count() { + const ret = instance.exports.bjs_ExplicitlyUncachedModel_count_get(this.pointer); + return ret; + } + set count(value) { + instance.exports.bjs_ExplicitlyUncachedModel_count_set(this.pointer, value); + } + } + const exports = { + CachedModel, + UncachedModel, + ExplicitlyUncachedModel, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js index 06cf6550e..613d4a10b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -193,12 +201,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_roundtrip"] = function bjs_roundtrip() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); } - arrayResult.reverse(); let ret = imports.roundtrip(arrayResult); for (const elem of ret) { i32Stack.push((elem | 0)); @@ -211,12 +224,17 @@ export async function createInstantiator(options, swift) { TestModule["bjs_logStrings"] = function bjs_logStrings() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const string = strStack.pop(); - arrayResult.push(string); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = strStack.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); } - arrayResult.reverse(); imports.logStrings(arrayResult); } catch (error) { setException(error); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js index c469fcb58..ab4b4b34d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -137,6 +138,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_FooContainer"] = function(objectId) { structHelpers.FooContainer.lower(swift.memory.getObject(objectId)); } @@ -281,14 +289,19 @@ export async function createInstantiator(options, swift) { i32Stack.push(foos.length); instance.exports.bjs_processFooArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const objId1 = i32Stack.pop(); - const obj = swift.memory.getObject(objId1); - swift.memory.release(objId1); - arrayResult.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + arrayResult.push(obj); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, processOptionalFooArray: function bjs_processOptionalFooArray(foos) { @@ -303,21 +316,26 @@ export async function createInstantiator(options, swift) { i32Stack.push(foos.length); instance.exports.bjs_processOptionalFooArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const isSome1 = i32Stack.pop(); - let optValue; - if (isSome1 === 0) { - optValue = null; - } else { - const objId1 = i32Stack.pop(); - const obj = swift.memory.getObject(objId1); - swift.memory.release(objId1); - optValue = obj; + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const isSome1 = i32Stack.pop(); + let optValue; + if (isSome1 === 0) { + optValue = null; + } else { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + optValue = obj; + } + arrayResult.push(optValue); } - arrayResult.push(optValue); + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, roundtripFooContainer: function bjs_roundtripFooContainer(container) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js index 952197c2a..59c8be11d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js index 88a5adb38..f3293ae52 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js index 10fafb7a0..ef666149b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.d.ts new file mode 100644 index 000000000..b842e7d7d --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.d.ts @@ -0,0 +1,21 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export type Exports = { + processBytes(data: Uint8Array): Uint8Array; + processFloats(data: Float32Array): Float32Array; + processGenericDoubles(data: Float64Array): Float64Array; + processGenericInts(data: Int32Array): Int32Array; +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.js new file mode 100644 index 000000000..b12640234 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSTypedArrayTypes.js @@ -0,0 +1,243 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + processBytes: function bjs_processBytes(data) { + const ret = instance.exports.bjs_processBytes(swift.memory.retain(data)); + const ret1 = swift.memory.getObject(ret); + swift.memory.release(ret); + return ret1; + }, + processFloats: function bjs_processFloats(data) { + const ret = instance.exports.bjs_processFloats(swift.memory.retain(data)); + const ret1 = swift.memory.getObject(ret); + swift.memory.release(ret); + return ret1; + }, + processGenericDoubles: function bjs_processGenericDoubles(data) { + const ret = instance.exports.bjs_processGenericDoubles(swift.memory.retain(data)); + const ret1 = swift.memory.getObject(ret); + swift.memory.release(ret); + return ret1; + }, + processGenericInts: function bjs_processGenericInts(data) { + const ret = instance.exports.bjs_processGenericInts(swift.memory.retain(data)); + const ret1 = swift.memory.getObject(ret); + swift.memory.release(ret); + return ret1; + }, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js index 08675da6a..71e66827e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -188,6 +189,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -302,15 +310,20 @@ export async function createInstantiator(options, swift) { TestModule["bjs_jsEchoJSValueArray"] = function bjs_jsEchoJSValueArray() { try { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const jsValuePayload2 = f64Stack.pop(); - const jsValuePayload1 = i32Stack.pop(); - const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); - arrayResult.push(jsValue); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const jsValuePayload2 = f64Stack.pop(); + const jsValuePayload1 = i32Stack.pop(); + const jsValueKind = i32Stack.pop(); + const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + arrayResult.push(jsValue); + } + arrayResult.reverse(); } - arrayResult.reverse(); let ret = imports.jsEchoJSValueArray(arrayResult); for (const elem of ret) { const [elemKind, elemPayload1, elemPayload2] = __bjs_jsValueLower(elem); @@ -342,18 +355,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -363,12 +397,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class JSValueHolder extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_JSValueHolder_deinit, JSValueHolder.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_JSValueHolder_deinit, JSValueHolder.prototype, null); } constructor(value, optionalValue) { @@ -531,15 +566,20 @@ export async function createInstantiator(options, swift) { i32Stack.push(values.length); instance.exports.bjs_roundTripJSValueArray(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const jsValuePayload2 = f64Stack.pop(); - const jsValuePayload1 = i32Stack.pop(); - const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); - arrayResult.push(jsValue); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const jsValuePayload2 = f64Stack.pop(); + const jsValuePayload1 = i32Stack.pop(); + const jsValueKind = i32Stack.pop(); + const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + arrayResult.push(jsValue); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; }, roundTripOptionalJSValueArray: function bjs_roundTripOptionalJSValueArray(values) { @@ -559,15 +599,20 @@ export async function createInstantiator(options, swift) { let optResult; if (isSome1) { const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const jsValuePayload2 = f64Stack.pop(); - const jsValuePayload1 = i32Stack.pop(); - const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); - arrayResult.push(jsValue); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const jsValuePayload2 = f64Stack.pop(); + const jsValuePayload1 = i32Stack.pop(); + const jsValueKind = i32Stack.pop(); + const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + arrayResult.push(jsValue); + } + arrayResult.reverse(); } - arrayResult.reverse(); optResult = arrayResult; } else { optResult = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js index f4fe4dd61..6c3ddb555 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -215,18 +223,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -236,12 +265,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class GlobalClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_GlobalAPI_GlobalClass_deinit, GlobalClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_GlobalAPI_GlobalClass_deinit, GlobalClass.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js index 4ce318f40..70f1575b4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -223,18 +231,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -244,12 +273,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class GlobalClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_GlobalAPI_GlobalClass_deinit, GlobalClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_GlobalAPI_GlobalClass_deinit, GlobalClass.prototype, null); } constructor() { @@ -265,7 +295,7 @@ export async function createInstantiator(options, swift) { } class PrivateClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PrivateAPI_PrivateClass_deinit, PrivateClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PrivateAPI_PrivateClass_deinit, PrivateClass.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js index 025a6fc8a..16ec9433c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -215,18 +223,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -236,12 +265,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class PrivateClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PrivateAPI_PrivateClass_deinit, PrivateClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PrivateAPI_PrivateClass_deinit, PrivateClass.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.d.ts index 4b7851c3e..1353220bc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.d.ts @@ -34,6 +34,7 @@ declare global { class Greeter { constructor(name: string); greet(): string; + makeDefault(): Greeter; release(): void; } class UUID { @@ -87,6 +88,8 @@ export type Exports = { Foundation: { Greeter: { new(name: string): Greeter; + makeDefault(): Greeter; + readonly defaultGreeting: string; } UUID: { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js index 4a6ee1990..d698857d3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -227,18 +235,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -248,12 +277,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Greeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_Greeter_deinit, Greeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_Greeter_deinit, Greeter.prototype, null); } constructor(name) { @@ -268,10 +298,20 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; } + static makeDefault() { + const ret = instance.exports.bjs___Swift_Foundation_Greeter_static_makeDefault(); + return Greeter.__construct(ret); + } + static get defaultGreeting() { + instance.exports.bjs___Swift_Foundation_Greeter_static_defaultGreeting_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converters_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converters_Converter_deinit, Converter.prototype, null); } constructor() { @@ -287,7 +327,7 @@ export async function createInstantiator(options, swift) { } class UUID extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_UUID_deinit, UUID.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_UUID_deinit, UUID.prototype, null); } uuidString() { @@ -299,7 +339,7 @@ export async function createInstantiator(options, swift) { } class Container extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Collections_Container_deinit, Container.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Collections_Container_deinit, Container.prototype, null); } constructor() { @@ -309,13 +349,18 @@ export async function createInstantiator(options, swift) { getItems() { instance.exports.bjs_Collections_Container_getItems(this.pointer); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const ptr = ptrStack.pop(); - const obj = Greeter.__construct(ptr); - arrayResult.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const ptr = ptrStack.pop(); + const obj = Greeter.__construct(ptr); + arrayResult.push(obj); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; } addItem(item) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.d.ts index fff65ce6d..6b2d65cd8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.d.ts @@ -47,6 +47,8 @@ export type Exports = { Foundation: { Greeter: { new(name: string): Greeter; + makeDefault(): Greeter; + readonly defaultGreeting: string; } UUID: { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js index 267d96152..92b8f5dae 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -227,18 +235,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -248,12 +277,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Greeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_Greeter_deinit, Greeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_Greeter_deinit, Greeter.prototype, null); } constructor(name) { @@ -268,10 +298,20 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; } + static makeDefault() { + const ret = instance.exports.bjs___Swift_Foundation_Greeter_static_makeDefault(); + return Greeter.__construct(ret); + } + static get defaultGreeting() { + instance.exports.bjs___Swift_Foundation_Greeter_static_defaultGreeting_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } } class Converter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converters_Converter_deinit, Converter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Utils_Converters_Converter_deinit, Converter.prototype, null); } constructor() { @@ -287,7 +327,7 @@ export async function createInstantiator(options, swift) { } class UUID extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_UUID_deinit, UUID.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs___Swift_Foundation_UUID_deinit, UUID.prototype, null); } uuidString() { @@ -299,7 +339,7 @@ export async function createInstantiator(options, swift) { } class Container extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Collections_Container_deinit, Container.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Collections_Container_deinit, Container.prototype, null); } constructor() { @@ -309,13 +349,18 @@ export async function createInstantiator(options, swift) { getItems() { instance.exports.bjs_Collections_Container_getItems(this.pointer); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const ptr = ptrStack.pop(); - const obj = Greeter.__construct(ptr); - arrayResult.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const ptr = ptrStack.pop(); + const obj = Greeter.__construct(ptr); + arrayResult.push(obj); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; } addItem(item) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.d.ts new file mode 100644 index 000000000..4e966661e --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.d.ts @@ -0,0 +1,46 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export namespace Player { + export interface Stats { + level: number; + rating: string; + } +} +export namespace User { + export interface Stats { + health: number; + score: number; + } +} +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface User extends SwiftHeapObject { + getName(): string; +} +export interface Player extends SwiftHeapObject { + getTag(): string; +} +export type Exports = { + User: { + } + Player: { + } +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.js new file mode 100644 index 000000000..33b4e60c1 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/NestedType.js @@ -0,0 +1,357 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + const __bjs_createUser_StatsHelpers = () => ({ + lower: (value) => { + i32Stack.push((value.health | 0)); + f64Stack.push(value.score); + }, + lift: () => { + const f64 = f64Stack.pop(); + const int = i32Stack.pop(); + return { health: int, score: f64 }; + } + }); + const __bjs_createPlayer_StatsHelpers = () => ({ + lower: (value) => { + i32Stack.push((value.level | 0)); + const bytes = textEncoder.encode(value.rating); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + }, + lift: () => { + const string = strStack.pop(); + const int = i32Stack.pop(); + return { level: int, rating: string }; + } + }); + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_struct_lower_User_Stats"] = function(objectId) { + structHelpers.User_Stats.lower(swift.memory.getObject(objectId)); + } + bjs["swift_js_struct_lift_User_Stats"] = function() { + const value = structHelpers.User_Stats.lift(); + return swift.memory.retain(value); + } + bjs["swift_js_struct_lower_Player_Stats"] = function(objectId) { + structHelpers.Player_Stats.lower(swift.memory.getObject(objectId)); + } + bjs["swift_js_struct_lift_Player_Stats"] = function() { + const value = structHelpers.Player_Stats.lift(); + return swift.memory.retain(value); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_Player_wrap"] = function(pointer) { + const obj = _exports['Player'].__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_User_wrap"] = function(pointer) { + const obj = _exports['User'].__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.hasReleased) { + return; + } + state.hasReleased = true; + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + }); + + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); + } + + release() { + const state = this.__swiftHeapObjectState; + if (state.hasReleased) { + return; + } + state.hasReleased = true; + swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); + state.deinit(state.pointer); + } + } + class User extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_User_deinit, User.prototype, null); + } + + getName() { + instance.exports.bjs_User_getName(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + } + class Player extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Player_deinit, Player.prototype, null); + } + + getTag() { + instance.exports.bjs_Player_getTag(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + } + const User_StatsHelpers = __bjs_createUser_StatsHelpers(); + structHelpers.User_Stats = User_StatsHelpers; + + const Player_StatsHelpers = __bjs_createPlayer_StatsHelpers(); + structHelpers.Player_Stats = Player_StatsHelpers; + + const exports = { + User, + Player, + Player: { + }, + User: { + }, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts index a5a6e16fb..fb9d68db7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts @@ -30,6 +30,7 @@ export interface WithOptionalJSClass { roundTripBoolOrUndefined(value: boolean | undefined): boolean | undefined; roundTripIntOrNull(value: number | null): number | null; roundTripIntOrUndefined(value: number | undefined): number | undefined; + roundTripChildOrNull(value: WithOptionalJSClass | null): WithOptionalJSClass | null; stringOrNull: string | null; stringOrUndefined: string | undefined; doubleOrNull: number | null; @@ -38,6 +39,7 @@ export interface WithOptionalJSClass { boolOrUndefined: boolean | undefined; intOrNull: number | null; intOrUndefined: number | undefined; + childOrNull: WithOptionalJSClass | null; } export type Exports = { Greeter: { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js index 37408a42d..f376c1b24 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -296,6 +304,19 @@ export async function createInstantiator(options, swift) { setException(error); } } + TestModule["bjs_WithOptionalJSClass_childOrNull_get"] = function bjs_WithOptionalJSClass_childOrNull_get(self) { + try { + let ret = swift.memory.getObject(self).childOrNull; + const isSome = ret != null; + if (isSome) { + const objId = swift.memory.retain(ret); + i32Stack.push(objId); + } + i32Stack.push(isSome ? 1 : 0); + } catch (error) { + setException(error); + } + } TestModule["bjs_WithOptionalJSClass_stringOrNull_set"] = function bjs_WithOptionalJSClass_stringOrNull_set(self, newValueIsSome, newValueBytes, newValueCount) { try { let optResult; @@ -366,6 +387,22 @@ export async function createInstantiator(options, swift) { setException(error); } } + TestModule["bjs_WithOptionalJSClass_childOrNull_set"] = function bjs_WithOptionalJSClass_childOrNull_set(self, newValue) { + try { + let optResult; + if (newValue) { + const objId = i32Stack.pop(); + const obj = swift.memory.getObject(objId); + swift.memory.release(objId); + optResult = obj; + } else { + optResult = null; + } + swift.memory.getObject(self).childOrNull = optResult; + } catch (error) { + setException(error); + } + } TestModule["bjs_WithOptionalJSClass_roundTripStringOrNull"] = function bjs_WithOptionalJSClass_roundTripStringOrNull(self, valueIsSome, valueBytes, valueCount) { try { let optResult; @@ -452,6 +489,28 @@ export async function createInstantiator(options, swift) { setException(error); } } + TestModule["bjs_WithOptionalJSClass_roundTripChildOrNull"] = function bjs_WithOptionalJSClass_roundTripChildOrNull(self, value) { + try { + let optResult; + if (value) { + const objId = i32Stack.pop(); + const obj = swift.memory.getObject(objId); + swift.memory.release(objId); + optResult = obj; + } else { + optResult = null; + } + let ret = swift.memory.getObject(self).roundTripChildOrNull(optResult); + const isSome = ret != null; + if (isSome) { + const objId1 = swift.memory.retain(ret); + i32Stack.push(objId1); + } + i32Stack.push(isSome ? 1 : 0); + } catch (error) { + setException(error); + } + } }, setInstance: (i) => { instance = i; @@ -471,18 +530,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -492,12 +572,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Greeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype, null); } constructor(name) { @@ -558,7 +639,7 @@ export async function createInstantiator(options, swift) { } class OptionalPropertyHolder extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_OptionalPropertyHolder_deinit, OptionalPropertyHolder.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_OptionalPropertyHolder_deinit, OptionalPropertyHolder.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js index 490f2b4e2..97c1a44fe 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js index bec07b959..a140ea232 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js index 658702a39..b8116a32f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -215,18 +223,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -236,12 +265,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class PropertyHolder extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyHolder_deinit, PropertyHolder.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyHolder_deinit, PropertyHolder.prototype, null); } constructor(intValue, floatValue, doubleValue, boolValue, stringValue, jsObject) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.d.ts index 4c09ad85f..27cd9212b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.d.ts @@ -93,6 +93,7 @@ export interface MyViewController extends SwiftHeapObject { export interface DelegateManager extends SwiftHeapObject { notifyAll(): void; delegates: MyViewControllerDelegate[]; + delegatesByName: Record; } export type Exports = { Helper: { @@ -105,6 +106,7 @@ export type Exports = { new(delegates: MyViewControllerDelegate[]): DelegateManager; } processDelegates(delegates: MyViewControllerDelegate[]): MyViewControllerDelegate[]; + processDelegatesByName(delegates: Record): Record; Direction: DirectionObject ExampleEnum: ExampleEnumObject Result: ResultObject diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js index e606c3a77..d992bf75d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js @@ -49,6 +49,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -155,6 +156,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -575,18 +583,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -596,12 +625,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Helper extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Helper_deinit, Helper.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Helper_deinit, Helper.prototype, null); } constructor(value) { @@ -621,7 +651,7 @@ export async function createInstantiator(options, swift) { } class MyViewController extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MyViewController_deinit, MyViewController.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MyViewController_deinit, MyViewController.prototype, null); } constructor(delegate) { @@ -682,7 +712,7 @@ export async function createInstantiator(options, swift) { } class DelegateManager extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DelegateManager_deinit, DelegateManager.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DelegateManager_deinit, DelegateManager.prototype, null); } constructor(delegates) { @@ -700,14 +730,19 @@ export async function createInstantiator(options, swift) { get delegates() { instance.exports.bjs_DelegateManager_delegates_get(this.pointer); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const objId = i32Stack.pop(); - const obj = swift.memory.getObject(objId); - swift.memory.release(objId); - arrayResult.push(obj); + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const objId = i32Stack.pop(); + const obj = swift.memory.getObject(objId); + swift.memory.release(objId); + arrayResult.push(obj); + } + arrayResult.reverse(); } - arrayResult.reverse(); return arrayResult; } set delegates(value) { @@ -718,6 +753,33 @@ export async function createInstantiator(options, swift) { i32Stack.push(value.length); instance.exports.bjs_DelegateManager_delegates_set(this.pointer); } + get delegatesByName() { + instance.exports.bjs_DelegateManager_delegatesByName_get(this.pointer); + const dictLen = i32Stack.pop(); + const dictResult = {}; + for (let i = 0; i < dictLen; i++) { + const objId = i32Stack.pop(); + const obj = swift.memory.getObject(objId); + swift.memory.release(objId); + const string = strStack.pop(); + dictResult[string] = obj; + } + return dictResult; + } + set delegatesByName(value) { + const entries = Object.entries(value); + for (const entry of entries) { + const [key, value1] = entry; + const bytes = textEncoder.encode(key); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const objId = swift.memory.retain(value1); + i32Stack.push(objId); + } + i32Stack.push(entries.length); + instance.exports.bjs_DelegateManager_delegatesByName_set(this.pointer); + } } const ResultHelpers = __bjs_createResultValuesHelpers(); enumHelpers.Result = ResultHelpers; @@ -734,15 +796,44 @@ export async function createInstantiator(options, swift) { i32Stack.push(delegates.length); instance.exports.bjs_processDelegates(); const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { + let arrayResult; + if (arrayLen === -1) { + arrayResult = taStack.pop(); + } else { + arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const objId1 = i32Stack.pop(); + const obj = swift.memory.getObject(objId1); + swift.memory.release(objId1); + arrayResult.push(obj); + } + arrayResult.reverse(); + } + return arrayResult; + }, + processDelegatesByName: function bjs_processDelegatesByName(delegates) { + const entries = Object.entries(delegates); + for (const entry of entries) { + const [key, value] = entry; + const bytes = textEncoder.encode(key); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const objId = swift.memory.retain(value); + i32Stack.push(objId); + } + i32Stack.push(entries.length); + instance.exports.bjs_processDelegatesByName(); + const dictLen = i32Stack.pop(); + const dictResult = {}; + for (let i = 0; i < dictLen; i++) { const objId1 = i32Stack.pop(); const obj = swift.memory.getObject(objId1); swift.memory.release(objId1); - arrayResult.push(obj); + const string = strStack.pop(); + dictResult[string] = obj; } - arrayResult.reverse(); - return arrayResult; + return dictResult; }, Direction: DirectionValues, ExampleEnum: ExampleEnumValues, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ProtocolInClosure.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ProtocolInClosure.js index 13070a3cc..89f84d29a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ProtocolInClosure.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ProtocolInClosure.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -123,6 +124,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -361,18 +369,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -382,12 +411,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Widget extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Widget_deinit, Widget.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Widget_deinit, Widget.prototype, null); } constructor(name) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js index 42e25545e..32a739587 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js @@ -36,6 +36,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -142,6 +143,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -259,18 +267,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -280,12 +309,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class MathUtils extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MathUtils_deinit, MathUtils.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MathUtils_deinit, MathUtils.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js index 4cf9615fb..16cf2881f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js @@ -36,6 +36,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -142,6 +143,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -259,18 +267,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -280,12 +309,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class MathUtils extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MathUtils_deinit, MathUtils.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MathUtils_deinit, MathUtils.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js index 9928804eb..b616665ca 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js @@ -30,6 +30,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -103,6 +104,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -220,18 +228,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -241,12 +270,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class PropertyClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyClass_deinit, PropertyClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyClass_deinit, PropertyClass.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js index f82ac20df..f6e1fdbce 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js @@ -30,6 +30,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -103,6 +104,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -220,18 +228,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -241,12 +270,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class PropertyClass extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyClass_deinit, PropertyClass.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyClass_deinit, PropertyClass.prototype, null); } constructor() { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js index 033f08cd2..885c0980f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js index 8187b9e92..aab8b67fe 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js index cf9faa707..88f04efe9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -243,18 +251,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -264,12 +293,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Greeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype, null); } constructor(name) { @@ -325,13 +355,13 @@ export async function createInstantiator(options, swift) { } class PublicGreeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PublicGreeter_deinit, PublicGreeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PublicGreeter_deinit, PublicGreeter.prototype, null); } } class PackageGreeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PackageGreeter_deinit, PackageGreeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PackageGreeter_deinit, PackageGreeter.prototype, null); } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.d.ts index ccc95eb3b..d024be7dd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.d.ts @@ -41,6 +41,9 @@ export const APIResultValues: { export type APIResultTag = { tag: typeof APIResultValues.Tag.Success; param0: string } | { tag: typeof APIResultValues.Tag.Failure; param0: number } | { tag: typeof APIResultValues.Tag.Flag; param0: boolean } | { tag: typeof APIResultValues.Tag.Rate; param0: number } | { tag: typeof APIResultValues.Tag.Precise; param0: number } | { tag: typeof APIResultValues.Tag.Info } +export interface Animal { + type: string; +} export type DirectionObject = typeof DirectionValues; export type ThemeObject = typeof ThemeValues; @@ -67,6 +70,8 @@ export type Exports = { TestProcessor: { new(transform: (arg0: string) => string): TestProcessor; } + roundtripAnimal(animalClosure: (arg0: Animal) => Animal): (arg0: Animal) => Animal; + roundtripOptionalAnimal(animalClosure: (arg0: Animal | null) => Animal | null): (arg0: Animal | null) => Animal | null; roundtripString(stringClosure: (arg0: string) => string): (arg0: string) => string; roundtripInt(intClosure: (arg0: number) => number): (arg0: number) => number; roundtripBool(boolClosure: (arg0: boolean) => boolean): (arg0: boolean) => boolean; @@ -92,6 +97,9 @@ export type Exports = { Theme: ThemeObject HttpStatus: HttpStatusObject APIResult: APIResultObject + Animal: { + init(type: string): Animal; + } } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js index 7e90c9415..c82bc5b8d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js @@ -55,6 +55,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -85,6 +86,18 @@ export async function createInstantiator(options, swift) { return swift.memory.retain(real); }; + const __bjs_createAnimalHelpers = () => ({ + lower: (value) => { + const bytes = textEncoder.encode(value.type); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + }, + lift: () => { + const string = strStack.pop(); + return { type: string }; + } + }); const __bjs_createAPIResultValuesHelpers = () => ({ lower: (value) => { const enumTag = value.tag; @@ -214,6 +227,20 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_struct_lower_Animal"] = function(objectId) { + structHelpers.Animal.lower(swift.memory.getObject(objectId)); + } + bjs["swift_js_struct_lift_Animal"] = function() { + const value = structHelpers.Animal.lift(); + return swift.memory.retain(value); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; @@ -359,6 +386,31 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule5ThemeO_5ThemeO); } + bjs["invoke_js_callback_TestModule_10TestModule6AnimalV_6AnimalV"] = function(callbackId) { + try { + const callback = swift.memory.getObject(callbackId); + const structValue = structHelpers.Animal.lift(); + let ret = callback(structValue); + structHelpers.Animal.lower(ret); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule6AnimalV_6AnimalV = function(param0) { + structHelpers.Animal.lower(param0); + instance.exports.invoke_swift_closure_TestModule_10TestModule6AnimalV_6AnimalV(boxPtr); + const structValue = structHelpers.Animal.lift(); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + return structValue; + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule6AnimalV_6AnimalV); + } bjs["invoke_js_callback_TestModule_10TestModule6PersonC_6PersonC"] = function(callbackId, param0) { try { const callback = swift.memory.getObject(callbackId); @@ -620,6 +672,46 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO); } + bjs["invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + let optResult; + if (param0) { + const struct = structHelpers.Animal.lift(); + optResult = struct; + } else { + optResult = null; + } + let ret = callback(optResult); + const isSome = ret != null; + if (isSome) { + structHelpers.Animal.lower(ret); + } + i32Stack.push(isSome ? 1 : 0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV = function(param0) { + const isSome = param0 != null; + if (isSome) { + structHelpers.Animal.lower(param0); + } + i32Stack.push(+isSome); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(boxPtr); + const isSome1 = i32Stack.pop(); + const optResult = isSome1 ? structHelpers.Animal.lift() : null; + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + return optResult; + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV); + } bjs["invoke_js_callback_TestModule_10TestModuleSq6PersonC_Sq6PersonC"] = function(callbackId, param0IsSome, param0Pointer) { try { const callback = swift.memory.getObject(callbackId); @@ -902,18 +994,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -923,12 +1036,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Person extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Person_deinit, Person.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Person_deinit, Person.prototype, null); } constructor(name) { @@ -940,7 +1054,7 @@ export async function createInstantiator(options, swift) { } class TestProcessor extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_TestProcessor_deinit, TestProcessor.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_TestProcessor_deinit, TestProcessor.prototype, null); } constructor(transform) { @@ -949,12 +1063,25 @@ export async function createInstantiator(options, swift) { return TestProcessor.__construct(ret); } } + const AnimalHelpers = __bjs_createAnimalHelpers(); + structHelpers.Animal = AnimalHelpers; + const APIResultHelpers = __bjs_createAPIResultValuesHelpers(); enumHelpers.APIResult = APIResultHelpers; const exports = { Person, TestProcessor, + roundtripAnimal: function bjs_roundtripAnimal(animalClosure) { + const callbackId = swift.memory.retain(animalClosure); + const ret = instance.exports.bjs_roundtripAnimal(callbackId); + return swift.memory.getObject(ret); + }, + roundtripOptionalAnimal: function bjs_roundtripOptionalAnimal(animalClosure) { + const callbackId = swift.memory.retain(animalClosure); + const ret = instance.exports.bjs_roundtripOptionalAnimal(callbackId); + return swift.memory.getObject(ret); + }, roundtripString: function bjs_roundtripString(stringClosure) { const callbackId = swift.memory.retain(stringClosure); const ret = instance.exports.bjs_roundtripString(callbackId); @@ -1064,6 +1191,15 @@ export async function createInstantiator(options, swift) { Theme: ThemeValues, HttpStatus: HttpStatusValues, APIResult: APIResultValues, + Animal: { + init: function(type) { + const typeBytes = textEncoder.encode(type); + const typeId = swift.memory.retain(typeBytes); + instance.exports.bjs_Animal_init(typeId, typeBytes.length); + const structValue = structHelpers.Animal.lift(); + return structValue; + }, + }, }; _exports = exports; return exports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js index d9df868ec..cffbdcf67 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -124,6 +125,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js index a60615686..d55d5c095 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js @@ -30,6 +30,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -311,6 +312,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_DataPoint"] = function(objectId) { structHelpers.DataPoint.lower(swift.memory.getObject(objectId)); } @@ -484,18 +492,39 @@ export async function createInstantiator(options, swift) { return; } state.hasReleased = true; + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); }); /// Represents a Swift heap object like a class instance or an actor instance. class SwiftHeapObject { - static __wrap(pointer, deinit, prototype) { - const obj = Object.create(prototype); - const state = { pointer, deinit, hasReleased: false }; - obj.pointer = pointer; - obj.__swiftHeapObjectState = state; - swiftHeapObjectFinalizationRegistry.register(obj, state, state); - return obj; + static __wrap(pointer, deinit, prototype, identityCache) { + const makeFresh = (identityMap) => { + const obj = Object.create(prototype); + const state = { pointer, deinit, hasReleased: false, identityMap }; + obj.pointer = pointer; + obj.__swiftHeapObjectState = state; + swiftHeapObjectFinalizationRegistry.register(obj, state, state); + if (identityMap) { + identityMap.set(pointer, new WeakRef(obj)); + } + return obj; + }; + + if (!identityCache) { + return makeFresh(null); + } + + const cached = identityCache.get(pointer)?.deref(); + if (cached && !cached.__swiftHeapObjectState.hasReleased) { + deinit(pointer); + return cached; + } + if (identityCache.has(pointer)) { + identityCache.delete(pointer); + } + + return makeFresh(identityCache); } release() { @@ -505,12 +534,13 @@ export async function createInstantiator(options, swift) { } state.hasReleased = true; swiftHeapObjectFinalizationRegistry.unregister(state); + state.identityMap?.delete(state.pointer); state.deinit(state.pointer); } } class Greeter extends SwiftHeapObject { static __construct(ptr) { - return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype); + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype, null); } constructor(name) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.d.ts index 3677f1e44..e97b50fda 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.d.ts @@ -12,6 +12,7 @@ export type Exports = { } export type Imports = { translate(point: Point, dx: number, dy: number): Point; + roundTripOptional(point: Point | null): Point | null; } export function createInstantiator(options: { imports: Imports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js index d4f1160f3..17bf086ff 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -110,6 +111,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_Point"] = function(objectId) { structHelpers.Point.lower(swift.memory.getObject(objectId)); } @@ -218,6 +226,25 @@ export async function createInstantiator(options, swift) { setException(error); } } + TestModule["bjs_roundTripOptional"] = function bjs_roundTripOptional(point) { + try { + let optResult; + if (point) { + const struct = structHelpers.Point.lift(); + optResult = struct; + } else { + optResult = null; + } + let ret = imports.roundTripOptional(optResult); + const isSome = ret != null; + if (isSome) { + structHelpers.Point.lower(ret); + } + i32Stack.push(isSome ? 1 : 0); + } catch (error) { + setException(error); + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts new file mode 100644 index 000000000..99adf95b6 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts @@ -0,0 +1,33 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export interface JSPublicEvent { +} +export interface JSPackageEvent { +} +export interface JSInternalEvent { +} +export interface JSPublicTarget { + addPublicListener(handler: (arg0: JSPublicEvent) => void): void; + addInternalListener(handler: (arg0: JSPublicEvent) => void): void; +} +export interface JSPackageTarget { + addPackageListener(handler: (arg0: JSPackageEvent) => void): void; +} +export interface JSInternalTarget { + addInternalListener(handler: (arg0: JSInternalEvent) => void): void; +} +export type Exports = { +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js new file mode 100644 index 000000000..2b51ebd3b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js @@ -0,0 +1,337 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + let taStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule13JSPublicEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule13JSPublicEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule13JSPublicEventC_y); + } + bjs["invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule14JSPackageEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule14JSPackageEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule14JSPackageEventC_y); + } + bjs["invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule15JSInternalEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule15JSInternalEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule15JSInternalEventC_y); + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_JSPublicTarget_addPublicListener"] = function bjs_JSPublicTarget_addPublicListener(self, handler) { + try { + swift.memory.getObject(self).addPublicListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSPublicTarget_addInternalListener"] = function bjs_JSPublicTarget_addInternalListener(self, handler) { + try { + swift.memory.getObject(self).addInternalListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSPackageTarget_addPackageListener"] = function bjs_JSPackageTarget_addPackageListener(self, handler) { + try { + swift.memory.getObject(self).addPackageListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSInternalTarget_addInternalListener"] = function bjs_JSInternalTarget_addInternalListener(self, handler) { + try { + swift.memory.getObject(self).addInternalListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js index b2c381a03..9c41c3061 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -98,6 +99,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js index ef81ef69e..97a00c278 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -115,6 +116,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_struct_lower_PointerFields"] = function(objectId) { structHelpers.PointerFields.lower(swift.memory.getObject(objectId)); } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js index 97948b286..2951ef5f8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js @@ -25,6 +25,7 @@ export async function createInstantiator(options, swift) { let f32Stack = []; let f64Stack = []; let ptrStack = []; + let taStack = []; const enumHelpers = {}; const structHelpers = {}; @@ -99,6 +100,13 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_i64"] = function() { return i64Stack.pop(); } + const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array]; + bjs["swift_js_push_typed_array"] = function(kind, ptr, count) { + const Ctor = taCtors[kind]; + const byteLen = count * Ctor.BYTES_PER_ELEMENT; + const copy = memory.buffer.slice(ptr, ptr + byteLen); + taStack.push(Array.from(new Ctor(copy))); + } bjs["swift_js_return_optional_bool"] = function(isSome, value) { if (isSome === 0) { tmpRetOptionalBool = null; diff --git a/Plugins/PackageToJS/Sources/BridgeJSPluginUtilities b/Plugins/PackageToJS/Sources/BridgeJSPluginUtilities new file mode 120000 index 000000000..97aa4a1cc --- /dev/null +++ b/Plugins/PackageToJS/Sources/BridgeJSPluginUtilities @@ -0,0 +1 @@ +../../BridgeJS/Sources/BridgeJSPluginUtilities \ No newline at end of file diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift index 7e335c68e..7686372f9 100644 --- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift +++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift @@ -748,15 +748,15 @@ class SkeletonCollector { } } if let target = target as? SwiftSourceModuleTarget { - let directories = [ - target.directoryURL.appending(path: "Generated/JavaScript"), - // context.pluginWorkDirectoryURL: ".build/plugins/PackageToJS/outputs/" - // .build/plugins/outputs/[package]/[target]/destination/BridgeJS/JavaScript/BridgeJS.json - context.pluginWorkDirectoryURL.deletingLastPathComponent().deletingLastPathComponent() - .appending(path: "outputs/\(package.id)/\(target.name)/destination/BridgeJS/JavaScript"), + let candidates = [ + target.directoryURL.appending(path: "Generated/JavaScript").appending(path: skeletonFile), + BridgeJSPluginPaths.skeletonURL( + targetName: target.name, + packageID: package.id, + commandPluginWorkDirectoryURL: context.pluginWorkDirectoryURL + ), ] - for directory in directories { - let skeletonURL = directory.appending(path: skeletonFile) + for skeletonURL in candidates { if FileManager.default.fileExists(atPath: skeletonURL.path) { skeletons.append(skeletonURL) } diff --git a/Plugins/PackageToJS/Templates/instantiate.js b/Plugins/PackageToJS/Templates/instantiate.js index 3dda6b28e..88e322538 100644 --- a/Plugins/PackageToJS/Templates/instantiate.js +++ b/Plugins/PackageToJS/Templates/instantiate.js @@ -68,6 +68,7 @@ async function createInstantiator(options, swift) { swift_js_push_i64: unexpectedBjsCall, swift_js_pop_i64: unexpectedBjsCall, swift_js_closure_unregister: unexpectedBjsCall, + swift_js_push_typed_array: unexpectedBjsCall, }; }, /** @param {WebAssembly.Instance} instance */ diff --git a/Plugins/PackageToJS/Templates/runtime.d.ts b/Plugins/PackageToJS/Templates/runtime.d.ts index 353db3894..e4795bedf 100644 --- a/Plugins/PackageToJS/Templates/runtime.d.ts +++ b/Plugins/PackageToJS/Templates/runtime.d.ts @@ -2,14 +2,16 @@ type ref = number; type pointer = number; declare class JSObjectSpace { - private _heapValueById; - private _heapEntryByValue; - private _heapNextKey; + private _slotByValue; + private _values; + private _stateBySlot; + private _freeSlotStack; constructor(); retain(value: any): number; - retainByRef(ref: ref): number; - release(ref: ref): void; - getObject(ref: ref): any; + retainByRef(reference: ref): number; + release(reference: ref): void; + getObject(reference: ref): any; + private _getValidatedSlotState; } /** @@ -97,6 +99,10 @@ declare class ITCInterface { sendingContext: pointer; transfer: Transferable[]; }; + invokeRemoteJSObjectBody(invocationContext: pointer): { + object: undefined; + transfer: Transferable[]; + }; release(objectRef: ref): { object: undefined; transfer: Transferable[]; @@ -140,6 +146,8 @@ type ResponseMessage = { sourceTid: number; /** The context pointer of the request */ context: pointer; + /** The request method this response corresponds to */ + requestMethod: keyof ITCInterface; /** The response content */ response: { ok: true; diff --git a/Plugins/PackageToJS/Templates/runtime.mjs b/Plugins/PackageToJS/Templates/runtime.mjs index d79275476..daf4f3ab0 100644 --- a/Plugins/PackageToJS/Templates/runtime.mjs +++ b/Plugins/PackageToJS/Templates/runtime.mjs @@ -135,6 +135,9 @@ class ITCInterface { const transfer = transferringObjects.map((ref) => this.memory.getObject(ref)); return { object: objects, sendingContext, transfer }; } + invokeRemoteJSObjectBody(invocationContext) { + return { object: undefined, transfer: [] }; + } release(objectRef) { this.memory.release(objectRef); return { object: undefined, transfer: [] }; @@ -238,44 +241,91 @@ function deserializeError(error) { const globalVariable = globalThis; +const SLOT_BITS = 24; +const SLOT_MASK = (1 << SLOT_BITS) - 1; +const GEN_MASK = (1 << (32 - SLOT_BITS)) - 1; class JSObjectSpace { constructor() { - this._heapValueById = new Map(); - this._heapValueById.set(1, globalVariable); - this._heapEntryByValue = new Map(); - this._heapEntryByValue.set(globalVariable, { id: 1, rc: 1 }); + this._slotByValue = new Map(); + this._values = []; + this._stateBySlot = []; + this._freeSlotStack = []; // Note: 0 is preserved for invalid references, 1 is preserved for globalThis - this._heapNextKey = 2; + this._values[0] = undefined; + this._values[1] = globalVariable; + this._slotByValue.set(globalVariable, 1); + this._stateBySlot[1] = 1; // gen=0, rc=1 } retain(value) { - const entry = this._heapEntryByValue.get(value); - if (entry) { - entry.rc++; - return entry.id; - } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id: id, rc: 1 }); - return id; - } - retainByRef(ref) { - return this.retain(this.getObject(ref)); - } - release(ref) { - const value = this._heapValueById.get(ref); - const entry = this._heapEntryByValue.get(value); - entry.rc--; - if (entry.rc != 0) + const slot = this._slotByValue.get(value); + if (slot !== undefined) { + const state = this._stateBySlot[slot]; + const nextState = (state + 1) >>> 0; + if ((nextState & SLOT_MASK) === 0) { + throw new RangeError(`Reference count overflow at slot ${slot}`); + } + this._stateBySlot[slot] = nextState; + return ((nextState & ~SLOT_MASK) | slot) >>> 0; + } + let newSlot; + let state; + if (this._freeSlotStack.length > 0) { + newSlot = this._freeSlotStack.pop(); + const gen = this._stateBySlot[newSlot] >>> SLOT_BITS; + state = ((gen << SLOT_BITS) | 1) >>> 0; + } + else { + newSlot = this._values.length; + if (newSlot > SLOT_MASK) { + throw new RangeError(`Reference slot overflow: ${newSlot} exceeds ${SLOT_MASK}`); + } + state = 1; + } + this._stateBySlot[newSlot] = state; + this._values[newSlot] = value; + this._slotByValue.set(value, newSlot); + return ((state & ~SLOT_MASK) | newSlot) >>> 0; + } + retainByRef(reference) { + const state = this._getValidatedSlotState(reference); + const slot = reference & SLOT_MASK; + const nextState = (state + 1) >>> 0; + if ((nextState & SLOT_MASK) === 0) { + throw new RangeError(`Reference count overflow at slot ${slot}`); + } + this._stateBySlot[slot] = nextState; + return reference; + } + release(reference) { + const state = this._getValidatedSlotState(reference); + const slot = reference & SLOT_MASK; + if ((state & SLOT_MASK) > 1) { + this._stateBySlot[slot] = (state - 1) >>> 0; return; - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); + } + this._slotByValue.delete(this._values[slot]); + this._values[slot] = undefined; + const nextGen = ((state >>> SLOT_BITS) + 1) & GEN_MASK; + this._stateBySlot[slot] = (nextGen << SLOT_BITS) >>> 0; + this._freeSlotStack.push(slot); + } + getObject(reference) { + this._getValidatedSlotState(reference); + return this._values[reference & SLOT_MASK]; } - getObject(ref) { - const value = this._heapValueById.get(ref); - if (value === undefined) { - throw new ReferenceError("Attempted to read invalid reference " + ref); + // Returns the packed state for the slot, after validating the reference. + _getValidatedSlotState(reference) { + const slot = reference & SLOT_MASK; + if (slot === 0) + throw new ReferenceError(`Attempted to use invalid reference ${reference}`); + const state = this._stateBySlot[slot]; + if (state === undefined || (state & SLOT_MASK) === 0) { + throw new ReferenceError(`Attempted to use invalid reference ${reference}`); } - return value; + if (state >>> SLOT_BITS !== reference >>> SLOT_BITS) { + throw new ReferenceError(`Attempted to use stale reference ${reference}`); + } + return state; } } @@ -455,13 +505,51 @@ class SwiftRuntime { if (broker) return broker; const itcInterface = new ITCInterface(this.memory); + const defaultRequestHandler = (message) => { + const request = message.data.request; + // @ts-ignore dynamic dispatch by method name + const result = itcInterface[request.method].apply(itcInterface, request.parameters); + return { ok: true, value: result }; + }; + const requestHandlers = { + invokeRemoteJSObjectBody: (message) => { + const invocationContext = message.data.request + .parameters[0]; + const hasError = this.exports.swjs_invoke_remote_jsobject_body(invocationContext); + return { + ok: true, + value: { + object: hasError, + sendingContext: message.data.context, + transfer: [], + }, + }; + }, + }; + const defaultResponseHandler = (message) => { + if (message.data.response.ok) { + const object = this.memory.retain(message.data.response.value.object); + this.exports.swjs_receive_response(object, message.data.context); + } + else { + const error = deserializeError(message.data.response.error); + const errorObject = this.memory.retain(error); + this.exports.swjs_receive_error(errorObject, message.data.context); + } + }; + const responseHandlers = { + invokeRemoteJSObjectBody: (_message) => { + // Swift continuation is resumed on the owner thread. + }, + }; const newBroker = new MessageBroker((_a = this.tid) !== null && _a !== void 0 ? _a : -1, threadChannel, { onRequest: (message) => { + var _a; let returnValue; try { - // @ts-ignore - const result = itcInterface[message.data.request.method](...message.data.request.parameters); - returnValue = { ok: true, value: result }; + const method = message.data.request.method; + const handler = (_a = requestHandlers[method]) !== null && _a !== void 0 ? _a : defaultRequestHandler; + returnValue = handler(message); } catch (error) { returnValue = { @@ -474,6 +562,7 @@ class SwiftRuntime { data: { sourceTid: message.data.sourceTid, context: message.data.context, + requestMethod: message.data.request.method, response: returnValue, }, }; @@ -489,15 +578,10 @@ class SwiftRuntime { } }, onResponse: (message) => { - if (message.data.response.ok) { - const object = this.memory.retain(message.data.response.value.object); - this.exports.swjs_receive_response(object, message.data.context); - } - else { - const error = deserializeError(message.data.response.error); - const errorObject = this.memory.retain(error); - this.exports.swjs_receive_error(errorObject, message.data.context); - } + var _a; + const method = message.data.requestMethod; + const handler = (_a = responseHandlers[method]) !== null && _a !== void 0 ? _a : defaultResponseHandler; + handler(message); }, }); broker = newBroker; @@ -842,6 +926,25 @@ class SwiftRuntime { }, }); }, + swjs_request_remote_jsobject_body: (object_source_tid, invocation_context) => { + var _a; + if (!this.options.threadChannel) { + throw new Error("threadChannel is not set in options given to SwiftRuntime. Please set it to request remote JSObject access."); + } + const broker = getMessageBroker(this.options.threadChannel); + broker.request({ + type: "request", + data: { + sourceTid: (_a = this.tid) !== null && _a !== void 0 ? _a : MAIN_THREAD_TID, + targetTid: object_source_tid, + context: invocation_context, + request: { + method: "invokeRemoteJSObjectBody", + parameters: [invocation_context], + }, + }, + }); + }, }; } postMessageToMainThread(message, transfer = []) { diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 5d6fe258f..7d75a6801 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -11,6 +11,7 @@ import { deserializeError, MainToWorkerMessage, MessageBroker, + RequestMessage, ResponseMessage, ITCInterface, serializeError, @@ -265,29 +266,98 @@ export class SwiftRuntime { const getMessageBroker = (threadChannel: SwiftRuntimeThreadChannel) => { if (broker) return broker; const itcInterface = new ITCInterface(this.memory); + type ITCMethodName = keyof ITCInterface; + + const defaultRequestHandler = ( + message: RequestMessage, + ): ResponseMessage["data"]["response"] => { + const request = message.data.request; + // @ts-ignore dynamic dispatch by method name + const result = itcInterface[request.method].apply( + itcInterface, + request.parameters as any[], + ); + return { ok: true, value: result }; + }; + + const requestHandlers: Partial< + Record< + ITCMethodName, + ( + message: RequestMessage, + ) => ResponseMessage["data"]["response"] + > + > = { + invokeRemoteJSObjectBody: (message) => { + const invocationContext = message.data.request + .parameters[0] as pointer; + const hasError = + this.exports.swjs_invoke_remote_jsobject_body( + invocationContext, + ); + return { + ok: true, + value: { + object: hasError, + sendingContext: message.data.context, + transfer: [], + }, + }; + }, + }; + + const defaultResponseHandler = (message: ResponseMessage) => { + if (message.data.response.ok) { + const object = this.memory.retain( + message.data.response.value.object, + ); + this.exports.swjs_receive_response( + object, + message.data.context, + ); + } else { + const error = deserializeError(message.data.response.error); + const errorObject = this.memory.retain(error); + this.exports.swjs_receive_error( + errorObject, + message.data.context, + ); + } + }; + + const responseHandlers: Partial< + Record void> + > = { + invokeRemoteJSObjectBody: (_message) => { + // Swift continuation is resumed on the owner thread. + }, + }; + const newBroker = new MessageBroker(this.tid ?? -1, threadChannel, { onRequest: (message) => { let returnValue: ResponseMessage["data"]["response"]; try { - // @ts-ignore - const result = itcInterface[ - message.data.request.method - ](...message.data.request.parameters); - returnValue = { ok: true, value: result }; + const method = message.data.request.method; + const handler = + requestHandlers[method] ?? defaultRequestHandler; + returnValue = handler(message); } catch (error) { returnValue = { ok: false, error: serializeError(error), }; } + const responseMessage: ResponseMessage = { type: "response", data: { sourceTid: message.data.sourceTid, context: message.data.context, + requestMethod: message.data.request.method, response: returnValue, }, }; + try { newBroker.reply(responseMessage); } catch (error) { @@ -303,24 +373,10 @@ export class SwiftRuntime { } }, onResponse: (message) => { - if (message.data.response.ok) { - const object = this.memory.retain( - message.data.response.value.object, - ); - this.exports.swjs_receive_response( - object, - message.data.context, - ); - } else { - const error = deserializeError( - message.data.response.error, - ); - const errorObject = this.memory.retain(error); - this.exports.swjs_receive_error( - errorObject, - message.data.context, - ); - } + const method = message.data.requestMethod; + const handler = + responseHandlers[method] ?? defaultResponseHandler; + handler(message); }, }); broker = newBroker; @@ -934,6 +990,29 @@ export class SwiftRuntime { }, }); }, + swjs_request_remote_jsobject_body: ( + object_source_tid: number, + invocation_context: pointer, + ) => { + if (!this.options.threadChannel) { + throw new Error( + "threadChannel is not set in options given to SwiftRuntime. Please set it to request remote JSObject access.", + ); + } + const broker = getMessageBroker(this.options.threadChannel); + broker.request({ + type: "request", + data: { + sourceTid: this.tid ?? MAIN_THREAD_TID, + targetTid: object_source_tid, + context: invocation_context, + request: { + method: "invokeRemoteJSObjectBody", + parameters: [invocation_context], + }, + }, + }); + }, }; } diff --git a/Runtime/src/itc.ts b/Runtime/src/itc.ts index 9fadff54a..5d110762b 100644 --- a/Runtime/src/itc.ts +++ b/Runtime/src/itc.ts @@ -117,6 +117,13 @@ export class ITCInterface { return { object: objects, sendingContext, transfer }; } + invokeRemoteJSObjectBody(invocationContext: pointer): { + object: undefined; + transfer: Transferable[]; + } { + return { object: undefined, transfer: [] }; + } + release(objectRef: ref): { object: undefined; transfer: Transferable[] } { this.memory.release(objectRef); return { object: undefined, transfer: [] }; @@ -163,6 +170,8 @@ export type ResponseMessage = { sourceTid: number; /** The context pointer of the request */ context: pointer; + /** The request method this response corresponds to */ + requestMethod: keyof ITCInterface; /** The response content */ response: | { diff --git a/Runtime/src/object-heap.ts b/Runtime/src/object-heap.ts index ba9cf8021..6138953e9 100644 --- a/Runtime/src/object-heap.ts +++ b/Runtime/src/object-heap.ts @@ -1,59 +1,114 @@ import { globalVariable } from "./find-global.js"; import { ref } from "./types.js"; -type SwiftRuntimeHeapEntry = { - id: number; - rc: number; -}; +const SLOT_BITS = 24; +const SLOT_MASK = (1 << SLOT_BITS) - 1; +const GEN_MASK = (1 << (32 - SLOT_BITS)) - 1; + export class JSObjectSpace { - private _heapValueById: Map; - private _heapEntryByValue: Map; - private _heapNextKey: number; + private _slotByValue: Map; + private _values: (any | undefined)[]; + private _stateBySlot: number[]; + private _freeSlotStack: number[]; constructor() { - this._heapValueById = new Map(); - this._heapValueById.set(1, globalVariable); - - this._heapEntryByValue = new Map(); - this._heapEntryByValue.set(globalVariable, { id: 1, rc: 1 }); + this._slotByValue = new Map(); + this._values = []; + this._stateBySlot = []; + this._freeSlotStack = []; // Note: 0 is preserved for invalid references, 1 is preserved for globalThis - this._heapNextKey = 2; + this._values[0] = undefined; + this._values[1] = globalVariable; + this._slotByValue.set(globalVariable, 1); + this._stateBySlot[1] = 1; // gen=0, rc=1 } retain(value: any) { - const entry = this._heapEntryByValue.get(value); - if (entry) { - entry.rc++; - return entry.id; + const slot = this._slotByValue.get(value); + if (slot !== undefined) { + const state = this._stateBySlot[slot]!; + const nextState = (state + 1) >>> 0; + if ((nextState & SLOT_MASK) === 0) { + throw new RangeError( + `Reference count overflow at slot ${slot}`, + ); + } + this._stateBySlot[slot] = nextState; + return ((nextState & ~SLOT_MASK) | slot) >>> 0; + } + + let newSlot: number; + let state: number; + if (this._freeSlotStack.length > 0) { + newSlot = this._freeSlotStack.pop()!; + const gen = this._stateBySlot[newSlot]! >>> SLOT_BITS; + state = ((gen << SLOT_BITS) | 1) >>> 0; + } else { + newSlot = this._values.length; + if (newSlot > SLOT_MASK) { + throw new RangeError( + `Reference slot overflow: ${newSlot} exceeds ${SLOT_MASK}`, + ); + } + state = 1; } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id: id, rc: 1 }); - return id; + + this._stateBySlot[newSlot] = state; + this._values[newSlot] = value; + this._slotByValue.set(value, newSlot); + return ((state & ~SLOT_MASK) | newSlot) >>> 0; } - retainByRef(ref: ref) { - return this.retain(this.getObject(ref)); + retainByRef(reference: ref) { + const state = this._getValidatedSlotState(reference); + const slot = reference & SLOT_MASK; + const nextState = (state + 1) >>> 0; + if ((nextState & SLOT_MASK) === 0) { + throw new RangeError(`Reference count overflow at slot ${slot}`); + } + this._stateBySlot[slot] = nextState; + return reference; } - release(ref: ref) { - const value = this._heapValueById.get(ref); - const entry = this._heapEntryByValue.get(value)!; - entry.rc--; - if (entry.rc != 0) return; + release(reference: ref) { + const state = this._getValidatedSlotState(reference); + const slot = reference & SLOT_MASK; + if ((state & SLOT_MASK) > 1) { + this._stateBySlot[slot] = (state - 1) >>> 0; + return; + } + + this._slotByValue.delete(this._values[slot]); + this._values[slot] = undefined; + const nextGen = ((state >>> SLOT_BITS) + 1) & GEN_MASK; + this._stateBySlot[slot] = (nextGen << SLOT_BITS) >>> 0; + this._freeSlotStack.push(slot); + } - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); + getObject(reference: ref) { + this._getValidatedSlotState(reference); + return this._values[reference & SLOT_MASK]; } - getObject(ref: ref) { - const value = this._heapValueById.get(ref); - if (value === undefined) { + // Returns the packed state for the slot, after validating the reference. + private _getValidatedSlotState(reference: ref): number { + const slot = reference & SLOT_MASK; + if (slot === 0) + throw new ReferenceError( + `Attempted to use invalid reference ${reference}`, + ); + const state = this._stateBySlot[slot]; + if (state === undefined || (state & SLOT_MASK) === 0) { + throw new ReferenceError( + `Attempted to use invalid reference ${reference}`, + ); + } + if (state >>> SLOT_BITS !== reference >>> SLOT_BITS) { throw new ReferenceError( - "Attempted to read invalid reference " + ref, + `Attempted to use stale reference ${reference}`, ); } - return value; + return state; } } diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index b39e949b2..bc83fcd22 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -22,6 +22,7 @@ export interface ExportedFunctions { swjs_wake_worker_thread(): void; swjs_receive_response(object: ref, transferring: pointer): void; swjs_receive_error(error: ref, context: number): void; + swjs_invoke_remote_jsobject_body(context: pointer): number; } export const enum LibraryFeatures { diff --git a/Sources/JavaScriptEventLoop/JSRemote.swift b/Sources/JavaScriptEventLoop/JSRemote.swift new file mode 100644 index 000000000..4f488d7b8 --- /dev/null +++ b/Sources/JavaScriptEventLoop/JSRemote.swift @@ -0,0 +1,155 @@ +import _Concurrency +@_spi(JSObject_id) import JavaScriptKit +import _CJavaScriptKit + +/// A sendable handle for temporarily accessing a `JSObject` on its owning thread. +/// +/// `JSRemote` lets you share a reference to a JavaScript object across Swift concurrency +/// domains without transferring or cloning the object itself. Instead, the object stays +/// owned by its original JavaScript thread, and `withJSObject(_:)` schedules a closure to +/// run on that owner when needed. +/// +/// This is useful when you need occasional coordinated access to a JavaScript object from +/// another thread, but cannot or should not move the object with `JSSending`. +/// +/// - Note: `JSRemote` does not make the underlying `JSObject` itself thread-safe. The object +/// may only be touched inside `withJSObject(_:)`. +/// +/// ## Example +/// +/// ```swift +/// let document = JSObject.global.document.object! +/// let remoteDocument = JSRemote(document) +/// +/// let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) +/// let title = try await Task(executorPreference: executor) { +/// try await remoteDocument.withJSObject { document in +/// document.title.string ?? "" +/// } +/// }.value +/// ``` +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +public struct JSRemote: @unchecked Sendable { + private final class Storage { + let sourceObject: JSObject + let sourceTid: Int32 + + init(sourceObject: JSObject, sourceTid: Int32) { + self.sourceObject = sourceObject + self.sourceTid = sourceTid + } + } + + private let storage: Storage + + fileprivate init(sourceObject: JSObject, sourceTid: Int32) { + self.storage = Storage(sourceObject: sourceObject, sourceTid: sourceTid) + } +} + +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +extension JSRemote where T == JSObject { + /// Creates a remote handle for a `JSObject`. + /// + /// The object remains owned by its current JavaScript thread. Access it later by calling + /// `withJSObject(_:)`, which executes the closure on the owning thread when necessary. + /// + /// ## Example + /// + /// ```swift + /// let remoteWindow = JSRemote(JSObject.global) + /// ``` + /// + /// - Parameter object: The JavaScript object to reference remotely. + public init(_ object: JSObject) { + #if compiler(>=6.1) && _runtime(_multithreaded) + self.init(sourceObject: object, sourceTid: object.ownerTid) + #else + self.init(sourceObject: object, sourceTid: -1) + #endif + } + + /// Performs an operation with the underlying `JSObject` on its owning thread. + /// + /// If the caller is already running on the thread that owns the object, `body` executes + /// immediately. Otherwise, this method asynchronously requests execution on the owner and + /// resumes when the closure completes. + /// + /// Use this API when the object must stay on its original thread but a result derived from + /// that object needs to be produced in another Swift concurrency context. + /// + /// ## Example + /// + /// ```swift + /// let location = try await remoteWindow.withJSObject { window in + /// window.location.href.string ?? "" + /// } + /// ``` + /// + /// - Parameter body: A sendable closure that receives the owned `JSObject`. + /// - Returns: The value produced by `body`. + /// - Throws: Any error thrown by `body`. + public func withJSObject( + _ body: @Sendable @escaping (JSObject) throws(E) -> R + ) async throws(E) -> sending R { + #if compiler(>=6.1) && _runtime(_multithreaded) + if storage.sourceTid == swjs_get_worker_thread_id_cached() { + return try body(storage.sourceObject) + } + let result: Result = await withCheckedContinuation { continuation in + let context = _JSRemoteContext( + sourceObject: storage.sourceObject, + body: body, + continuation: continuation + ) + swjs_request_remote_jsobject_body( + storage.sourceTid, + Unmanaged.passRetained(context).toOpaque() + ) + } + return try result.get() + #else + return try body(storage.sourceObject) + #endif + } +} + +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +private final class _JSRemoteContext: @unchecked Sendable { + let invokeBody: () -> Bool + + init( + sourceObject: JSObject, + body: @escaping @Sendable (JSObject) throws(E) -> R, + continuation: CheckedContinuation, Never> + ) { + self.invokeBody = { + // NOTE: Sendability violation here for `sourceObject` + // Even though `JSObject` is not Sendable, it is safe to access it here + // because this invokeBody closure will only be executed on the owning thread. + do throws(E) { + continuation.resume(returning: .success(try body(sourceObject))) + } catch { + continuation.resume(returning: .failure(error)) + } + return false + } + } +} + +#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) + guard let contextPtr else { return true } + let context = Unmanaged<_JSRemoteContext>.fromOpaque(contextPtr).takeRetainedValue() + + return context.invokeBody() + #else + _ = contextPtr + return true + #endif +} diff --git a/Sources/JavaScriptEventLoop/JSSending.swift b/Sources/JavaScriptEventLoop/JSSending.swift index 7a3750c15..fb2fb1ddf 100644 --- a/Sources/JavaScriptEventLoop/JSSending.swift +++ b/Sources/JavaScriptEventLoop/JSSending.swift @@ -226,6 +226,32 @@ extension JSSending { /// - Parameter isolation: The actor isolation context for this call, used in Swift concurrency. /// - Returns: The received object of type `T`. /// - Throws: `JSSendingError` if the sending operation fails, or `JSException` if a JavaScript error occurs. + #if compiler(>=6.4) + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) + public func receive( + isolation: isolated (any Actor)? = #isolation, + file: StaticString = #file, + line: UInt = #line + ) async throws(JSException) -> T { + #if _runtime(_multithreaded) + let idInDestination = try await withCheckedThrowingContinuation { continuation in + let context = _JSSendingContext(continuation: continuation) + let idInSource = self.storage.idInSource + let transferring = self.storage.transferring ? [idInSource] : [] + swjs_request_sending_object( + idInSource, + transferring, + Int32(transferring.count), + self.storage.sourceTid, + Unmanaged.passRetained(context).toOpaque() + ) + } + return storage.construct(JSObject(id: idInDestination)) + #else + return storage.construct(storage.sourceObject) + #endif + } + #else @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func receive( isolation: isolated (any Actor)? = #isolation, @@ -250,6 +276,7 @@ extension JSSending { return storage.construct(storage.sourceObject) #endif } + #endif // 6.0 and below can't compile the following without a compiler crash. #if compiler(>=6.1) @@ -341,11 +368,19 @@ extension JSSending { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) private final class _JSSendingContext: Sendable { + #if compiler(>=6.4) + let continuation: CheckedContinuation + + init(continuation: CheckedContinuation) { + self.continuation = continuation + } + #else let continuation: CheckedContinuation init(continuation: CheckedContinuation) { self.continuation = continuation } + #endif } /// Error type representing failures during JavaScript object sending operations. diff --git a/Sources/JavaScriptEventLoop/JavaScriptEventLoop+ExecutorFactory.swift b/Sources/JavaScriptEventLoop/JavaScriptEventLoop+ExecutorFactory.swift index 7de4cb74a..0d2010016 100644 --- a/Sources/JavaScriptEventLoop/JavaScriptEventLoop+ExecutorFactory.swift +++ b/Sources/JavaScriptEventLoop/JavaScriptEventLoop+ExecutorFactory.swift @@ -4,14 +4,14 @@ // See: https://github.com/swiftlang/swift/pull/80266 // See: https://forums.swift.org/t/pitch-2-custom-main-and-global-executors/78437 -#if compiler(>=6.3) -@_spi(ExperimentalCustomExecutors) import _Concurrency +#if compiler(>=6.4) || (swift(>=6.3) && arch(wasm32)) +@_spi(ExperimentalCustomExecutors) @_spi(ExperimentalScheduling) import _Concurrency #else import _Concurrency -#endif +#endif // #if compiler(>=6.4) || (swift(>=6.3) && arch(wasm32)) import _CJavaScriptKit -#if compiler(>=6.3) +#if compiler(>=6.4) || (swift(>=6.3) && arch(wasm32)) // MARK: - MainExecutor Implementation // MainExecutor is used by the main actor to execute tasks on the main thread @@ -40,6 +40,22 @@ extension JavaScriptEventLoop: SchedulingExecutor { tolerance: C.Duration?, clock: C ) { + #if hasFeature(Embedded) + #if compiler(>=6.4) + // In Embedded Swift, ContinuousClock and SuspendingClock are unavailable. + // Hand-off the scheduling work to the Clock implementation for custom clocks. + clock.enqueue( + job, + on: self, + at: clock.now.advanced(by: delay), + tolerance: tolerance + ) + #else + fatalError( + "Delayed enqueue requires Swift 6.4+ in Embedded mode" + ) + #endif // #if compiler(>=6.4) (Embedded) + #else // #if hasFeature(Embedded) let duration: Duration // Handle clocks we know if let _ = clock as? ContinuousClock { @@ -47,7 +63,9 @@ extension JavaScriptEventLoop: SchedulingExecutor { } else if let _ = clock as? SuspendingClock { duration = delay as! SuspendingClock.Duration } else { - // Hand-off the scheduling work to Clock implementation for unknown clocks + #if compiler(>=6.4) + // Hand-off the scheduling work to Clock implementation for unknown clocks. + // Clock.enqueue is only available in the development branch (6.4+). clock.enqueue( job, on: self, @@ -55,12 +73,16 @@ extension JavaScriptEventLoop: SchedulingExecutor { tolerance: tolerance ) return + #else + fatalError("Unsupported clock type; only ContinuousClock and SuspendingClock are supported") + #endif // #if compiler(>=6.4) (non-Embedded) } let milliseconds = Self.delayInMilliseconds(from: duration) self.enqueue( UnownedJob(job), withDelay: milliseconds ) + #endif // #if hasFeature(Embedded) } private static func delayInMilliseconds(from swiftDuration: Duration) -> Double { @@ -111,4 +133,4 @@ extension JavaScriptEventLoop: ExecutorFactory { } } -#endif // compiler(>=6.3) +#endif // #if compiler(>=6.4) || (swift(>=6.3) && arch(wasm32)) diff --git a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift index aebc90d65..5fc267ddc 100644 --- a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift +++ b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift @@ -1,5 +1,5 @@ import JavaScriptKit -#if compiler(>=6.3) +#if compiler(>=6.4) || (swift(>=6.3) && arch(wasm32)) @_spi(ExperimentalCustomExecutors) import _Concurrency #else import _Concurrency @@ -123,13 +123,16 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable { private static func installGlobalExecutorIsolated() { guard !didInstallGlobalExecutor else { return } didInstallGlobalExecutor = true - #if compiler(>=6.3) + #if (compiler(>=6.4) || (swift(>=6.3) && arch(wasm32))) && !hasFeature(Embedded) if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999, *) { - // For Swift 6.3 and above, we can use the new `ExecutorFactory` API + // For Swift 6.4 and above, we can use the new `ExecutorFactory` API _Concurrency._createExecutors(factory: JavaScriptEventLoop.self) } #else - // For Swift 6.1 and below, we need to install the global executor by hook API + // For Swift 6.1 and below, or 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. installByLegacyHook() #endif } diff --git a/Sources/JavaScriptFoundationCompat/Data+JSValue.swift b/Sources/JavaScriptFoundationCompat/Data+JSValue.swift index ac8e773b4..c4408d8cf 100644 --- a/Sources/JavaScriptFoundationCompat/Data+JSValue.swift +++ b/Sources/JavaScriptFoundationCompat/Data+JSValue.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import JavaScriptKit /// Data <-> Uint8Array conversion. The conversion is lossless and copies the bytes at most once per conversion @@ -18,7 +22,7 @@ extension Data: ConvertibleToJSValue, ConstructibleFromJSValue { public var jsValue: JSValue { jsTypedArray.jsValue } /// Construct a Data from a JSTypedArray. - public static func construct(from uint8Array: JSTypedArray) -> Data? { + public static func construct(from uint8Array: JSTypedArray) -> Data { // First, allocate the data storage var data = Data(count: uint8Array.lengthInBytes) // Then, copy the byte contents into the Data buffer diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift index dceecf5bf..4717b6705 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift @@ -98,7 +98,7 @@ public final class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiter /// used as the return value for the `withUnsafeBytes(_:)` method. The /// argument is valid only for the duration of the closure's execution. /// - Returns: The return value, if any, of the `body` closure parameter. - public func withUnsafeBytes(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R { + public func withUnsafeBytes(_ body: (UnsafeBufferPointer) throws(E) -> R) throws(E) -> R { let buffer = UnsafeMutableBufferPointer.allocate(capacity: length) defer { buffer.deallocate() } copyMemory(to: buffer) @@ -121,7 +121,9 @@ public final class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiter /// argument is valid only for the duration of the closure's execution. /// - Returns: The return value, if any, of the `body`async closure parameter. @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public func withUnsafeBytesAsync(_ body: (UnsafeBufferPointer) async throws -> R) async rethrows -> R { + public func withUnsafeBytesAsync( + _ body: (UnsafeBufferPointer) async throws(E) -> R + ) async throws(E) -> R { let buffer = UnsafeMutableBufferPointer.allocate(capacity: length) defer { buffer.deallocate() } copyMemory(to: buffer) @@ -202,3 +204,12 @@ public enum JSUInt8Clamped: TypedArrayElement { } public typealias JSUInt8ClampedArray = JSTypedArray + +public typealias JSInt8Array = JSTypedArray +public typealias JSUint8Array = JSTypedArray +public typealias JSInt16Array = JSTypedArray +public typealias JSUint16Array = JSTypedArray +public typealias JSInt32Array = JSTypedArray +public typealias JSUint32Array = JSTypedArray +public typealias JSFloat32Array = JSTypedArray +public typealias JSFloat64Array = JSTypedArray diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 180567ed1..ff586b45b 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -4,6 +4,7 @@ /// by the BridgeJS system. import _CJavaScriptKit +import _Concurrency #if !arch(wasm32) @usableFromInline func _onlyAvailableOnWasm() -> Never { @@ -153,6 +154,11 @@ public protocol _BridgedSwiftStackType { static func bridgeJSStackPopAsOptional() -> StackLiftResult? /// Specialization point for pushing an `Optional` static func bridgeJSStackPushAsOptional(_ value: consuming Self?) + + /// Specialization point for popping an `Array` from the bridge stack + static func bridgeJSStackPopAsArray() -> [StackLiftResult] + /// Specialization point for pushing an `Array` onto the bridge stack + static func bridgeJSStackPushAsArray(_ value: consuming [Self]) } extension _BridgedSwiftStackType { @@ -177,17 +183,36 @@ extension _BridgedSwiftStackType { _swift_js_push_i32(1) } } + + public static func bridgeJSStackPopAsArray() -> [StackLiftResult] { + let count = Int(_swift_js_pop_i32()) + var result: [StackLiftResult] = [] + result.reserveCapacity(count) + for _ in 0... -@_spi(BridgeJS) public protocol _BridgedAsOptional { +public protocol _BridgedAsOptional { associatedtype Wrapped var asOptional: Wrapped? { get } init(optional: Wrapped?) } -@_spi(BridgeJS) extension Optional: _BridgedAsOptional { +extension Optional: _BridgedAsOptional { public var asOptional: Wrapped? { self } public init(optional: Wrapped?) { self = optional } } @@ -846,6 +871,10 @@ extension _BridgedSwiftStruct { return Self(unsafelyCopying: jsObject) } + @_spi(BridgeJS) public static func bridgeJSLiftReturn() -> Self { + bridgeJSStackPop() + } + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { bridgeJSStackPop() } @@ -1003,6 +1032,107 @@ private func _swift_js_pop_f64_extern() -> Float64 { _swift_js_pop_f64_extern() } +// MARK: Typed array bulk push extern + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_push_typed_array") +private func _swift_js_push_typed_array_extern(_ kind: Int32, _ ptr: UnsafeRawPointer, _ count: Int32) +#else +private func _swift_js_push_typed_array_extern(_ kind: Int32, _ ptr: UnsafeRawPointer, _ count: Int32) { + _onlyAvailableOnWasm() +} +#endif + +/// Pushes a typed array onto the JS typed-array stack via bulk copy. +@_spi(BridgeJS) @inline(never) public func _swift_js_push_typed_array( + _ kind: Int32, + _ ptr: UnsafeRawPointer, + _ count: Int32 +) { + _swift_js_push_typed_array_extern(kind, ptr, count) +} + +/// Numeric TypedArray kind identifiers (must match the JS `taCtors` table in BridgeJSLink). +public enum _BridgedNumericArrayKind: Int32 { + case int8 = 0 + case uint8 = 1 + case int16 = 2 + case uint16 = 3 + case int32 = 4 + case uint32 = 5 + case float32 = 6 + case float64 = 7 +} + +// MARK: - Numeric typed-array bulk push + +/// Numeric types that opt into bulk typed-array push. +public protocol _BridgedNumericArray: _BridgedSwiftStackType where StackLiftResult == Self { + static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { get } +} + +extension _BridgedNumericArray { + @_spi(BridgeJS) + public static func bridgeJSStackPushAsArray(_ value: consuming [Self]) { + value.withUnsafeBufferPointer { buffer in + guard let base = buffer.baseAddress else { + _swift_js_push_i32(0) + return + } + _swift_js_push_typed_array( + Self._bridgedNumericArrayKind.rawValue, + UnsafeRawPointer(base), + Int32(buffer.count) + ) + // -1 discriminator tells JS arrayLift to pop from the typed-array stack + _swift_js_push_i32(-1) + } + } +} + +extension Int8: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .int8 } +} +extension UInt8: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .uint8 } +} +extension Int16: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .int16 } +} +extension UInt16: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .uint16 } +} +extension Int32: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .int32 } +} +extension UInt32: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .uint32 } +} +extension Float: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .float32 } +} +extension Double: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { .float64 } +} +extension Int: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { + #if _pointerBitWidth(_32) + return .int32 + #else + return .int32 + #endif + } +} +extension UInt: _BridgedNumericArray { + public static var _bridgedNumericArrayKind: _BridgedNumericArrayKind { + #if _pointerBitWidth(_32) + return .uint32 + #else + return .uint32 + #endif + } +} + // MARK: Wasm externs used by type lowering/lifting #if arch(wasm32) @@ -1692,11 +1822,7 @@ extension _BridgedAsOptional where Wrapped == JSObject { } @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - asOptional._bridgeJSLowerReturn( - noneValue: 0, - lowerWrapped: { $0.bridgeJSLowerReturn() }, - write: _swift_js_return_optional_object - ) + Wrapped.bridgeJSStackPushAsOptional(asOptional) } } @@ -1972,22 +2098,11 @@ extension Array: _BridgedSwiftStackType where Element: _BridgedSwiftStackType, E public typealias StackLiftResult = [Element] @_spi(BridgeJS) public static func bridgeJSStackPop() -> [Element] { - let count = Int(_swift_js_pop_i32()) - var result: [Element] = [] - result.reserveCapacity(count) - for _ in 0.. [Element] { @@ -2083,3 +2198,91 @@ extension _BridgedAsOptional { Wrapped.bridgeJSStackPushAsOptional(asOptional) } } + +// MARK: Async Promise Awaiting + +/// Protocol for type-erasing `JSTypedClosure` in `_bjs_awaitPromise`. +/// +/// The library cannot name concrete `JSTypedClosure<(Int) -> Void>` etc. because +/// those require per-module generated convenience inits. This protocol provides +/// access to the underlying JS object ref and cleanup. +@_spi(BridgeJS) public protocol _BridgeJSReleasableClosure { + var jsObject: JSObject { get } + func release() +} + +@_spi(BridgeJS) extension JSTypedClosure: _BridgeJSReleasableClosure {} + +/// Awaits a JavaScript Promise using typed resolve/reject `JSTypedClosure` callbacks. +/// +/// The closure factories are dependency-injected because this library cannot +/// reference per-module generated `make_swift_closure_*` externs. The generated +/// code passes `{ JSTypedClosure<(T) -> Void>($0) }` which uses the per-module +/// convenience init. +/// +/// - Parameters: +/// - makeResolveClosure: A factory that wraps a `(T) -> Void` Swift closure +/// into a typed `JSTypedClosure`, creating the corresponding JS function. +/// - makeRejectClosure: A factory that wraps a `(JSValue) -> Void` Swift closure +/// into a `JSTypedClosure`, for the rejection path. +/// - body: A closure that receives the resolve and reject JS object refs +/// (as `Int32`) and should pass them to the appropriate JS extern function. +/// - Returns: The resolved value of type `T` from the Promise. +/// - Throws: `JSException` if the Promise rejects. +@_spi(BridgeJS) public func _bjs_awaitPromise( + makeResolveClosure: (@escaping (sending T) -> Void) -> R, + makeRejectClosure: (@escaping (sending JSValue) -> Void) -> E, + _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void +) async throws(JSException) -> T { + var resolveClosure: R? + var rejectClosure: E? + let result: Result = await withCheckedContinuation { continuation in + let resolve = makeResolveClosure { value in + continuation.resume(returning: .success(value)) + } + let reject = makeRejectClosure { value in + continuation.resume(returning: .failure(JSException(value))) + } + resolveClosure = resolve + rejectClosure = reject + body( + Int32(bitPattern: resolve.jsObject.id), + Int32(bitPattern: reject.jsObject.id) + ) + } + resolveClosure?.release() + rejectClosure?.release() + return try result.get() +} + +/// Void-returning overload of `_bjs_awaitPromise`. +/// +/// Needed because `(Void) -> Void` is not the same as `() -> Void` in Swift (SE-0110), +/// so the generic overload cannot handle void returns. +@_spi(BridgeJS) public func _bjs_awaitPromise( + makeResolveClosure: (@escaping () -> Void) -> R, + makeRejectClosure: (@escaping (sending JSValue) -> Void) -> E, + _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void +) async throws(JSException) { + var resolveClosure: R? + var rejectClosure: E? + let error: JSException? = await withCheckedContinuation { continuation in + let resolve = makeResolveClosure { + continuation.resume(returning: nil) + } + let reject = makeRejectClosure { value in + continuation.resume(returning: JSException(value)) + } + resolveClosure = resolve + rejectClosure = reject + body( + Int32(bitPattern: resolve.jsObject.id), + Int32(bitPattern: reject.jsObject.id) + ) + } + resolveClosure?.release() + rejectClosure?.release() + if let error { + throw error + } +} diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/BridgeJS-Configuration.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/BridgeJS-Configuration.md index 604017aad..b8856ac30 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/BridgeJS-Configuration.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/BridgeJS-Configuration.md @@ -12,6 +12,8 @@ The configuration system supports two complementary files: - `bridge-js.config.json` - Base configuration (checked into version control) - `bridge-js.config.local.json` - Local overrides (intended to be ignored by git, for developer-specific settings) +> Note: The presence of a configuration file, even if empty, is required to expose `@JS` types to other modules in the package. See `Examples/MultiModule/` for an example. + ## Configuration Loading ### File Locations @@ -73,6 +75,36 @@ const greeter = new exports.MyModule.Greeter("World"); // globalThis.MyModule is undefined ``` +### `identityMode` + +Controls whether exported Swift class instances use pointer-based identity mapping. + +When set to `"pointer"`, every class in the target uses identity caching — the same Swift heap pointer always returns the same JavaScript wrapper object. This makes `===` identity checks work across boundary crossings. + +```json +{ + "identityMode": "pointer" +} +``` + +**With `identityMode: "pointer"`:** + +```javascript +const a = exports.getModel(); +const b = exports.getModel(); // same Swift object +console.log(a === b); // true +``` + +**Without (default):** + +```javascript +const a = exports.getModel(); +const b = exports.getModel(); // same Swift object +console.log(a === b); // false — different JS wrapper each time +``` + +For finer control, use the `@JS(identityMode:)` parameter on individual classes instead of the project-wide config. See for details. + ### `tools` Specify custom paths for external executables. This is particularly useful when working in environments like Xcode where the system PATH may not be inherited, or when you need to use a specific version of tools for your project. diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Array.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Array.md index e97bceefa..1100358ba 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Array.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Array.md @@ -95,6 +95,42 @@ TypeScript definitions: - `[Int]?` becomes `number[] | null` - `[[Int]]` becomes `number[][]` +## Using TypedArrays + +When you need the JavaScript API to use native TypedArray types (e.g., `Uint8Array` for `fetch` body, `Float32Array` for WebGPU), use ``JSTypedArray`` instead of a plain Swift array: + +```swift +import JavaScriptKit + +@JS func processData(_ data: JSTypedArray) -> JSTypedArray { + return data +} + +// Convenience typealiases also work: +@JS func processFloats(_ data: JSFloat32Array) -> JSFloat32Array { + return data +} +``` + +Generated TypeScript: + +```typescript +export type Exports = { + processData(data: Uint8Array): Uint8Array; + processFloats(data: Float32Array): Float32Array; +} +``` + +Unlike plain arrays which use copy semantics, `JSTypedArray` uses **reference semantics** — it wraps a JavaScript TypedArray object and passes it by reference (no data copying). This is ideal for large binary data or when interacting with JavaScript APIs that expect TypedArrays. + +| Swift | TypeScript | +|:------|:-----------| +| `JSTypedArray` / `JSUint8Array` | `Uint8Array` | +| `JSTypedArray` / `JSInt8Array` | `Int8Array` | +| `JSTypedArray` / `JSInt32Array` | `Int32Array` | +| `JSTypedArray` / `JSFloat32Array` | `Float32Array` | +| `JSTypedArray` / `JSFloat64Array` | `Float64Array` | + ## How It Works Arrays use **copy semantics** when crossing the Swift/JavaScript boundary: diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md index a16c81286..8a1b7dff5 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md @@ -135,6 +135,46 @@ Classes use **reference semantics** when crossing the Swift/JavaScript boundary: This differs from structs, which use copy semantics and transfer data by value. +## Identity Mode + +By default, each boundary crossing creates a new JavaScript wrapper for the same Swift object. This means `===` identity checks fail even when the underlying Swift object is the same: + +```javascript +const a = exports.getModel(); +const b = exports.getModel(); // same Swift object +console.log(a === b); // false — different wrappers +``` + +For classes where wrapper identity matters, enable identity mode with the `identityMode` parameter: + +```swift +@JS(identityMode: true) +class Model { + @JS var name: String + @JS init(name: String) { self.name = name } +} +``` + +With identity mode, BridgeJS maintains a per-class `WeakRef`-based cache keyed by the Swift heap pointer. The same pointer always returns the same JavaScript wrapper: + +```javascript +const a = exports.getModel(); +const b = exports.getModel(); // same Swift object +console.log(a === b); // true — same wrapper +``` + +Identity mode is opt-in per class. Non-annotated classes have zero overhead. To enable it for all classes in a target, use `bridge-js.config.json` instead: + +```json +{ "identityMode": "pointer" } +``` + +Per-class `@JS(identityMode: true/false)` overrides the config setting. + +### Tradeoffs + +Identity mode improves performance for reuse-heavy workloads (same objects crossing repeatedly) but adds overhead for create-heavy workloads (many short-lived objects). The cache infrastructure (`Map`, `WeakRef`, `FinalizationRegistry`) has a per-object cost that is only worthwhile when objects are returned multiple times. + ## Supported Features | Swift Feature | Status | diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Setting-up-BridgeJS.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Setting-up-BridgeJS.md index 99a3d5b1c..5b9616105 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Setting-up-BridgeJS.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Setting-up-BridgeJS.md @@ -58,3 +58,7 @@ For package layout and how to consume the output from JavaScript, see . + +## Multiple targets in one package + +A single package can have multiple targets that use `@JS`. Apply the BridgeJS plugin to every target that contains `@JS` declarations. To make a target's `@JS` types visible to other targets in the same package, also add a `bridge-js.config.json` file (`{}` is enough) to that target’s source directory. See `Examples/MultiModule/` for an example. diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md index 81a135af3..5c609ab72 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md @@ -16,6 +16,20 @@ Swift types and their JavaScript/TypeScript equivalents at the BridgeJS boundary | ``JSUndefinedOr`` `` | `undefined` or `T` | `T \| undefined` | | ``JSObject`` | object | `object` | | ``JSValue`` | any | `any` | +| ``JSTypedArray`` `` | TypedArray | `Uint8Array`, `Float32Array`, etc. | + +### TypedArray mapping + +When using `JSTypedArray` (or convenience typealiases) in `@JS` signatures, the TypeScript type maps to the corresponding JavaScript TypedArray: + +| Swift | TypeScript | +|:--|:--| +| `JSTypedArray` / `JSUint8Array` | `Uint8Array` | +| `JSTypedArray` / `JSInt32Array` | `Int32Array` | +| `JSTypedArray` / `JSFloat32Array` | `Float32Array` | +| `JSTypedArray` / `JSFloat64Array` | `Float64Array` | + +See for usage details. ## See Also diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Unsupported-Features.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Unsupported-Features.md index 7ba25f7ae..83213aca1 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Unsupported-Features.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Unsupported-Features.md @@ -8,13 +8,11 @@ BridgeJS generates glue code per Swift target (module). Some patterns that are v ## Type usage crossing module boundary -BridgeJS does **not** support using a type across module boundaries in the following situations. +### Exporting Swift: extending types from another Swift module -### Exporting Swift: types from another Swift module +If you have multiple Swift targets (e.g. a library and an app), you **cannot** extend a type defined in one target with a `@JS` exported API in another target. -If you have multiple Swift targets (e.g. a library and an app), you **cannot** use a type defined in one target in an exported API of another target. - -**Unsupported example:** Module `App` exports a function that takes or returns a type defined in module `Lib`: +**Unsupported example:** Module `App` extends a type defined in module `Lib`: ```swift // In module Lib @@ -24,5 +22,15 @@ If you have multiple Swift targets (e.g. a library and an app), you **cannot** u } // In module App (depends on Lib) - unsupported -@JS public func transform(_ p: LibPoint) -> LibPoint { ... } +extension LibPoint { + @JS public func transformed() -> LibPoint { ... } +} ``` + +### Exporting Swift: non-`@JS` types from another Swift module + +While using `@JS` types from another Swift module is supported, it is not possible to use non-`@JS` types defined in other modules: this will fail at type lookup. + +### Exporting Swift: types from another Swift package + +Types defined in a separate Swift package cannot yet be referenced from `@JS` declarations in your package. diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index 4e6a0a085..0eabfa78d 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -16,25 +16,26 @@ import _CJavaScriptKit /// public struct JSString: LosslessStringConvertible, Equatable { /// The internal representation of JS compatible string - /// The initializers of this type must initialize `jsRef` or `buffer`. + /// The initializers of this type must initialize `jsObject` or `buffer`. /// And the uninitialized one will be lazily initialized class Guts { - var shouldDeallocateRef: Bool = false - lazy var jsRef: JavaScriptObjectRef = { - self.shouldDeallocateRef = true - return buffer.withUTF8 { bufferPtr in + // Owns the JS-side ref via JSObject, whose deinit routes the release to + // the correct thread via swjs_release_remote when destroyed off-owner-thread. + lazy var jsObject: JSObject = { + let ref = buffer.withUTF8 { bufferPtr in return swjs_decode_string(bufferPtr.baseAddress!, Int32(bufferPtr.count)) } + return JSObject(id: ref) // captures ownerTid = current thread here }() lazy var buffer: String = { var bytesRef: JavaScriptObjectRef = 0 - let bytesLength = Int(swjs_encode_string(jsRef, &bytesRef)) + let bytesLength = Int(swjs_encode_string(jsObject.id, &bytesRef)) // +1 for null terminator let buffer = UnsafeMutablePointer.allocate(capacity: bytesLength + 1) defer { buffer.deallocate() - swjs_release(bytesRef) + swjs_release(bytesRef) // bytesRef is a same-thread temporary } swjs_load_string(bytesRef, buffer) buffer[bytesLength] = 0 @@ -46,13 +47,7 @@ public struct JSString: LosslessStringConvertible, Equatable { } init(from jsRef: JavaScriptObjectRef) { - self.jsRef = jsRef - self.shouldDeallocateRef = true - } - - deinit { - guard shouldDeallocateRef else { return } - swjs_release(jsRef) + self.jsObject = JSObject(id: jsRef) } } @@ -79,7 +74,7 @@ public struct JSString: LosslessStringConvertible, Equatable { public static func == (lhs: JSString, rhs: JSString) -> Bool { withExtendedLifetime(lhs.guts) { lhsGuts in withExtendedLifetime(rhs.guts) { rhsGuts in - return swjs_value_equals(lhsGuts.jsRef, rhsGuts.jsRef) + return swjs_value_equals(lhsGuts.jsObject.id, rhsGuts.jsObject.id) } } } @@ -95,6 +90,6 @@ extension JSString: ExpressibleByStringLiteral { extension JSString { func asInternalJSRef() -> JavaScriptObjectRef { - guts.jsRef + guts.jsObject.id } } diff --git a/Sources/JavaScriptKit/JSUndefinedOr.swift b/Sources/JavaScriptKit/JSUndefinedOr.swift index c4d601738..de7be09b4 100644 --- a/Sources/JavaScriptKit/JSUndefinedOr.swift +++ b/Sources/JavaScriptKit/JSUndefinedOr.swift @@ -53,4 +53,4 @@ extension JSUndefinedOr: ConvertibleToJSValue where Wrapped: ConvertibleToJSValu // MARK: - BridgeJS (via _BridgedAsOptional in BridgeJSIntrinsics) -@_spi(BridgeJS) extension JSUndefinedOr: _BridgedAsOptional {} +extension JSUndefinedOr: _BridgedAsOptional {} diff --git a/Sources/JavaScriptKit/Macros.swift b/Sources/JavaScriptKit/Macros.swift index 67b3488bf..3189cdeab 100644 --- a/Sources/JavaScriptKit/Macros.swift +++ b/Sources/JavaScriptKit/Macros.swift @@ -113,7 +113,8 @@ public enum JSImportFrom: String { /// /// - Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases. @attached(peer) -public macro JS(namespace: String? = nil, enumStyle: JSEnumStyle = .const) = Builtin.ExternalMacro +public macro JS(namespace: String? = nil, enumStyle: JSEnumStyle = .const, identityMode: Bool = false) = + Builtin.ExternalMacro /// A macro that generates a Swift getter that reads a value from JavaScript. /// diff --git a/Sources/JavaScriptKit/ThreadLocal.swift b/Sources/JavaScriptKit/ThreadLocal.swift index e92ca32ac..12bf78773 100644 --- a/Sources/JavaScriptKit/ThreadLocal.swift +++ b/Sources/JavaScriptKit/ThreadLocal.swift @@ -4,6 +4,8 @@ import wasi_pthread #endif #elseif canImport(Darwin) import Darwin +#elseif canImport(Android) +import Android #elseif canImport(Glibc) import Glibc #else diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 28e7b5e3d..3800a6d9e 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -355,4 +355,9 @@ IMPORT_JS_FUNCTION(swjs_request_sending_objects, void, (const JavaScriptObjectRe int object_source_tid, void * _Nonnull sending_context)) +/// Requests invoking a Swift closure associated with `invocation_context` on `object_source_tid`. +/// This must be called from a non-owner thread and will asynchronously notify completion. +IMPORT_JS_FUNCTION(swjs_request_remote_jsobject_body, void, (int object_source_tid, + void * _Nonnull invocation_context)) + #endif /* _CJavaScriptKit_h */ diff --git a/Tests/BridgeJSGlobalTests/Generated/BridgeJS.swift b/Tests/BridgeJSGlobalTests/Generated/BridgeJS.swift index 7816681da..4e35a1c9f 100644 --- a/Tests/BridgeJSGlobalTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSGlobalTests/Generated/BridgeJS.swift @@ -1,4 +1,5 @@ // bridge-js: skip +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // @@ -21,7 +22,7 @@ extension GlobalNetworking.API.CallMethod: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -36,7 +37,7 @@ extension GlobalNetworking.API.CallMethod: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 @@ -70,7 +71,7 @@ extension Internal.SupportedServerMethod: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -81,7 +82,7 @@ extension Internal.SupportedServerMethod: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 diff --git a/Tests/BridgeJSGlobalTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSGlobalTests/Generated/JavaScript/BridgeJS.json index f57f91936..5e9626840 100644 --- a/Tests/BridgeJSGlobalTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSGlobalTests/Generated/JavaScript/BridgeJS.json @@ -559,5 +559,8 @@ ] }, - "moduleName" : "BridgeJSGlobalTests" + "moduleName" : "BridgeJSGlobalTests", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Tests/BridgeJSIdentityTests/Generated/BridgeJS.swift b/Tests/BridgeJSIdentityTests/Generated/BridgeJS.swift new file mode 100644 index 000000000..72a8dfdd4 --- /dev/null +++ b/Tests/BridgeJSIdentityTests/Generated/BridgeJS.swift @@ -0,0 +1,373 @@ +// bridge-js: skip +// swift-format-ignore-file +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +@_spi(BridgeJS) import JavaScriptKit + +@_expose(wasm, "bjs_getSharedSubject") +@_cdecl("bjs_getSharedSubject") +public func _bjs_getSharedSubject() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = getSharedSubject() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_resetSharedSubject") +@_cdecl("bjs_resetSharedSubject") +public func _bjs_resetSharedSubject() -> Void { + #if arch(wasm32) + resetSharedSubject() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getRetainLeakSubject") +@_cdecl("bjs_getRetainLeakSubject") +public func _bjs_getRetainLeakSubject() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = getRetainLeakSubject() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_resetRetainLeakSubject") +@_cdecl("bjs_resetRetainLeakSubject") +public func _bjs_resetRetainLeakSubject() -> Void { + #if arch(wasm32) + resetRetainLeakSubject() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getRetainLeakDeinits") +@_cdecl("bjs_getRetainLeakDeinits") +public func _bjs_getRetainLeakDeinits() -> Int32 { + #if arch(wasm32) + let ret = getRetainLeakDeinits() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_resetRetainLeakDeinits") +@_cdecl("bjs_resetRetainLeakDeinits") +public func _bjs_resetRetainLeakDeinits() -> Void { + #if arch(wasm32) + resetRetainLeakDeinits() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_setupArrayPool") +@_cdecl("bjs_setupArrayPool") +public func _bjs_setupArrayPool(_ count: Int32) -> Void { + #if arch(wasm32) + setupArrayPool(_: Int.bridgeJSLiftParameter(count)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getArrayPool") +@_cdecl("bjs_getArrayPool") +public func _bjs_getArrayPool() -> Void { + #if arch(wasm32) + let ret = getArrayPool() + ret.bridgeJSStackPush() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getArrayPoolElement") +@_cdecl("bjs_getArrayPoolElement") +public func _bjs_getArrayPoolElement(_ index: Int32) -> Void { + #if arch(wasm32) + let ret = getArrayPoolElement(_: Int.bridgeJSLiftParameter(index)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getArrayPoolDeinits") +@_cdecl("bjs_getArrayPoolDeinits") +public func _bjs_getArrayPoolDeinits() -> Int32 { + #if arch(wasm32) + let ret = getArrayPoolDeinits() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_resetArrayPoolDeinits") +@_cdecl("bjs_resetArrayPoolDeinits") +public func _bjs_resetArrayPoolDeinits() -> Void { + #if arch(wasm32) + resetArrayPoolDeinits() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_clearArrayPool") +@_cdecl("bjs_clearArrayPool") +public func _bjs_clearArrayPool() -> Void { + #if arch(wasm32) + clearArrayPool() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityTestSubject_init") +@_cdecl("bjs_IdentityTestSubject_init") +public func _bjs_IdentityTestSubject_init(_ value: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = IdentityTestSubject(value: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityTestSubject_value_get") +@_cdecl("bjs_IdentityTestSubject_value_get") +public func _bjs_IdentityTestSubject_value_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = IdentityTestSubject.bridgeJSLiftParameter(_self).value + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityTestSubject_value_set") +@_cdecl("bjs_IdentityTestSubject_value_set") +public func _bjs_IdentityTestSubject_value_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + IdentityTestSubject.bridgeJSLiftParameter(_self).value = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityTestSubject_currentValue_get") +@_cdecl("bjs_IdentityTestSubject_currentValue_get") +public func _bjs_IdentityTestSubject_currentValue_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = IdentityTestSubject.bridgeJSLiftParameter(_self).currentValue + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IdentityTestSubject_deinit") +@_cdecl("bjs_IdentityTestSubject_deinit") +public func _bjs_IdentityTestSubject_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension IdentityTestSubject: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_IdentityTestSubject_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_IdentityTestSubject_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSIdentityTests", name: "bjs_IdentityTestSubject_wrap") +fileprivate func _bjs_IdentityTestSubject_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_IdentityTestSubject_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_IdentityTestSubject_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_IdentityTestSubject_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_RetainLeakSubject_init") +@_cdecl("bjs_RetainLeakSubject_init") +public func _bjs_RetainLeakSubject_init(_ tag: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = RetainLeakSubject(tag: Int.bridgeJSLiftParameter(tag)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_RetainLeakSubject_tag_get") +@_cdecl("bjs_RetainLeakSubject_tag_get") +public func _bjs_RetainLeakSubject_tag_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = RetainLeakSubject.bridgeJSLiftParameter(_self).tag + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_RetainLeakSubject_tag_set") +@_cdecl("bjs_RetainLeakSubject_tag_set") +public func _bjs_RetainLeakSubject_tag_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + RetainLeakSubject.bridgeJSLiftParameter(_self).tag = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_RetainLeakSubject_deinit") +@_cdecl("bjs_RetainLeakSubject_deinit") +public func _bjs_RetainLeakSubject_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension RetainLeakSubject: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_RetainLeakSubject_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_RetainLeakSubject_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSIdentityTests", name: "bjs_RetainLeakSubject_wrap") +fileprivate func _bjs_RetainLeakSubject_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_RetainLeakSubject_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_RetainLeakSubject_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_RetainLeakSubject_wrap_extern(pointer) +} + +@_expose(wasm, "bjs_ArrayIdentityElement_init") +@_cdecl("bjs_ArrayIdentityElement_init") +public func _bjs_ArrayIdentityElement_init(_ tag: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ArrayIdentityElement(tag: Int.bridgeJSLiftParameter(tag)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ArrayIdentityElement_tag_get") +@_cdecl("bjs_ArrayIdentityElement_tag_get") +public func _bjs_ArrayIdentityElement_tag_get(_ _self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ArrayIdentityElement.bridgeJSLiftParameter(_self).tag + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ArrayIdentityElement_tag_set") +@_cdecl("bjs_ArrayIdentityElement_tag_set") +public func _bjs_ArrayIdentityElement_tag_set(_ _self: UnsafeMutableRawPointer, _ value: Int32) -> Void { + #if arch(wasm32) + ArrayIdentityElement.bridgeJSLiftParameter(_self).tag = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ArrayIdentityElement_deinit") +@_cdecl("bjs_ArrayIdentityElement_deinit") +public func _bjs_ArrayIdentityElement_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension ArrayIdentityElement: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_ArrayIdentityElement_wrap(Unmanaged.passRetained(self).toOpaque())))) + } + consuming func bridgeJSLowerAsProtocolReturn() -> Int32 { + _bjs_ArrayIdentityElement_wrap(Unmanaged.passRetained(self).toOpaque()) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSIdentityTests", name: "bjs_ArrayIdentityElement_wrap") +fileprivate func _bjs_ArrayIdentityElement_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_ArrayIdentityElement_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_ArrayIdentityElement_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + return _bjs_ArrayIdentityElement_wrap_extern(pointer) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSIdentityTests", name: "bjs_gc") +fileprivate func bjs_gc_extern() -> Void +#else +fileprivate func bjs_gc_extern() -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_gc() -> Void { + return bjs_gc_extern() +} + +func _$gc() throws(JSException) -> Void { + bjs_gc() + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSIdentityTests", name: "bjs_IdentityModeTestImports_runJsIdentityModeTests_static") +fileprivate func bjs_IdentityModeTestImports_runJsIdentityModeTests_static_extern() -> Void +#else +fileprivate func bjs_IdentityModeTestImports_runJsIdentityModeTests_static_extern() -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_IdentityModeTestImports_runJsIdentityModeTests_static() -> Void { + return bjs_IdentityModeTestImports_runJsIdentityModeTests_static_extern() +} + +func _$IdentityModeTestImports_runJsIdentityModeTests() throws(JSException) -> Void { + bjs_IdentityModeTestImports_runJsIdentityModeTests_static() + if let error = _swift_js_take_exception() { + throw error + } +} \ No newline at end of file diff --git a/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json new file mode 100644 index 000000000..56db0a3ed --- /dev/null +++ b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json @@ -0,0 +1,469 @@ +{ + "exported" : { + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_IdentityTestSubject_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "IdentityTestSubject", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "value", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "currentValue", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "IdentityTestSubject" + }, + { + "constructor" : { + "abiName" : "bjs_RetainLeakSubject_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "tag", + "name" : "tag", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "RetainLeakSubject", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "tag", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "RetainLeakSubject" + }, + { + "constructor" : { + "abiName" : "bjs_ArrayIdentityElement_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "tag", + "name" : "tag", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "ArrayIdentityElement", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "tag", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "ArrayIdentityElement" + } + ], + "enums" : [ + + ], + "exposeToGlobal" : false, + "functions" : [ + { + "abiName" : "bjs_getSharedSubject", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getSharedSubject", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "IdentityTestSubject" + } + } + }, + { + "abiName" : "bjs_resetSharedSubject", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "resetSharedSubject", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getRetainLeakSubject", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getRetainLeakSubject", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "RetainLeakSubject" + } + } + }, + { + "abiName" : "bjs_resetRetainLeakSubject", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "resetRetainLeakSubject", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getRetainLeakDeinits", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getRetainLeakDeinits", + "parameters" : [ + + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "abiName" : "bjs_resetRetainLeakDeinits", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "resetRetainLeakDeinits", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_setupArrayPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "setupArrayPool", + "parameters" : [ + { + "label" : "_", + "name" : "count", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getArrayPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getArrayPool", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "ArrayIdentityElement" + } + } + } + } + }, + { + "abiName" : "bjs_getArrayPoolElement", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getArrayPoolElement", + "parameters" : [ + { + "label" : "_", + "name" : "index", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "ArrayIdentityElement" + } + }, + "_1" : "null" + } + } + }, + { + "abiName" : "bjs_getArrayPoolDeinits", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getArrayPoolDeinits", + "parameters" : [ + + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "abiName" : "bjs_resetArrayPoolDeinits", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "resetArrayPoolDeinits", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_clearArrayPool", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "clearArrayPool", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ], + "identityMode" : "pointer", + "protocols" : [ + + ], + "structs" : [ + + ] + }, + "imported" : { + "children" : [ + { + "functions" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "from" : "global", + "name" : "gc", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ], + "types" : [ + { + "accessLevel" : "internal", + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "IdentityModeTestImports", + "setters" : [ + + ], + "staticMethods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "runJsIdentityModeTests", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ] + } + ] + } + ] + }, + "moduleName" : "BridgeJSIdentityTests", + "usedExternalModules" : [ + + ] +} \ No newline at end of file diff --git a/Tests/BridgeJSIdentityTests/IdentityModeTests.swift b/Tests/BridgeJSIdentityTests/IdentityModeTests.swift new file mode 100644 index 000000000..0aa036163 --- /dev/null +++ b/Tests/BridgeJSIdentityTests/IdentityModeTests.swift @@ -0,0 +1,153 @@ +import XCTest +import JavaScriptKit + +@JSFunction(from: .global) func gc() throws(JSException) -> Void + +@JSClass struct IdentityModeTestImports { + @JSFunction static func runJsIdentityModeTests() throws(JSException) +} + +final class IdentityModeTests: XCTestCase { + func testRunJsIdentityModeTests() throws { + try IdentityModeTestImports.runJsIdentityModeTests() + } + + /// Verifies that identity-cached wrappers are properly reclaimed by GC. + /// + /// Creates an identity-mode object, crosses it multiple times (filling the + /// identity cache), drops all references, triggers GC + event loop ticks, + /// and verifies the Swift object is deallocated. This proves that the + /// WeakRef-based identity cache does not prevent garbage collection. + func testIdentityCachedWrapperIsReclaimedByGC() async throws { + RetainLeakSubject.deinits = 0 + + // Create object and cross it multiple times to fill identity cache + _retainLeakSubject = RetainLeakSubject(tag: 99) + weak var weakSubject = _retainLeakSubject + + // Cross to JS 5 times (populates identity cache with WeakRef) + for _ in 0..<5 { + _ = getRetainLeakSubject() + } + + // Drop Swift-side strong reference + _retainLeakSubject = nil + + // JS wrapper should still be alive via the identity cache's WeakRef, + // but WeakRef doesn't prevent GC. Trigger GC + event loop ticks to + // let FinalizationRegistry fire and call deinit. + for _ in 0..<100 { + try gc() + try await Task.sleep(for: .milliseconds(0)) + if weakSubject == nil { + break + } + } + + // The identity-cached wrapper should have been collected, + // FinalizationRegistry should have fired, deinit should have run. + XCTAssertNil(weakSubject, "Identity-cached object should be deallocated after GC") + XCTAssertEqual(RetainLeakSubject.deinits, 1, "Deinit should fire exactly once") + } +} + +@JS class IdentityTestSubject { + @JS var value: Int + + @JS init(value: Int) { + self.value = value + } + + @JS var currentValue: Int { value } +} + +nonisolated(unsafe) private var _sharedSubject: IdentityTestSubject? + +@JS func getSharedSubject() -> IdentityTestSubject { + if _sharedSubject == nil { + _sharedSubject = IdentityTestSubject(value: 42) + } + return _sharedSubject! +} + +@JS func resetSharedSubject() { + _sharedSubject = nil +} + +@JS class RetainLeakSubject { + nonisolated(unsafe) static var deinits: Int = 0 + + @JS var tag: Int + + @JS init(tag: Int) { + self.tag = tag + } + + deinit { + Self.deinits += 1 + } +} + +nonisolated(unsafe) private var _retainLeakSubject: RetainLeakSubject? + +@JS func getRetainLeakSubject() -> RetainLeakSubject { + if _retainLeakSubject == nil { + _retainLeakSubject = RetainLeakSubject(tag: 1) + } + return _retainLeakSubject! +} + +@JS func resetRetainLeakSubject() { + _retainLeakSubject = nil +} + +@JS func getRetainLeakDeinits() -> Int { + RetainLeakSubject.deinits +} + +@JS func resetRetainLeakDeinits() { + RetainLeakSubject.deinits = 0 +} + +// MARK: - Array identity tests + +@JS class ArrayIdentityElement { + nonisolated(unsafe) static var deinits: Int = 0 + + @JS var tag: Int + + @JS init(tag: Int) { + self.tag = tag + } + + deinit { + Self.deinits += 1 + } +} + +nonisolated(unsafe) private var _arrayPool: [ArrayIdentityElement] = [] + +@JS func setupArrayPool(_ count: Int) { + _arrayPool = (0.. [ArrayIdentityElement] { + return _arrayPool +} + +@JS func getArrayPoolElement(_ index: Int) -> ArrayIdentityElement? { + guard index >= 0, index < _arrayPool.count else { return nil } + return _arrayPool[index] +} + +@JS func getArrayPoolDeinits() -> Int { + ArrayIdentityElement.deinits +} + +@JS func resetArrayPoolDeinits() { + ArrayIdentityElement.deinits = 0 +} + +@JS func clearArrayPool() { + _arrayPool = [] +} diff --git a/Tests/BridgeJSIdentityTests/JavaScript/IdentityModeTests.mjs b/Tests/BridgeJSIdentityTests/JavaScript/IdentityModeTests.mjs new file mode 100644 index 000000000..b2ae2187d --- /dev/null +++ b/Tests/BridgeJSIdentityTests/JavaScript/IdentityModeTests.mjs @@ -0,0 +1,200 @@ +// @ts-check + +import assert from "node:assert"; + +/** + * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports["IdentityModeTestImports"]} + */ +export function getImports(importsContext) { + return { + runJsIdentityModeTests: () => { + const exports = importsContext.getExports(); + if (!exports) { + throw new Error("No exports!?"); + } + runIdentityModeTests(exports); + }, + }; +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function runIdentityModeTests(exports) { + testWrapperIdentity(exports); + testCacheInvalidationOnRelease(exports); + testDifferentClassesDontCollide(exports); + testRetainLeakOnCacheHit(exports); + testArrayElementIdentity(exports); + testArrayElementMatchesSingleGetter(exports); + testArrayRetainLeak(exports); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testWrapperIdentity(exports) { + exports.resetSharedSubject(); + const a = exports.getSharedSubject(); + const b = exports.getSharedSubject(); + + assert.strictEqual( + a, + b, + "Same Swift object should return identical JS wrapper", + ); + assert.equal(a.currentValue, 42); + + a.release(); + exports.resetSharedSubject(); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testCacheInvalidationOnRelease(exports) { + exports.resetSharedSubject(); + const first = exports.getSharedSubject(); + first.release(); + + exports.resetSharedSubject(); + const second = exports.getSharedSubject(); + + assert.notStrictEqual( + first, + second, + "After release + reset, should get a different wrapper", + ); + assert.equal(second.currentValue, 42); + + second.release(); + exports.resetSharedSubject(); +} + +/** + * Verifies that repeated boundary crossings of the same Swift object don't leak + * retain counts. Each cache hit triggers passRetained on the Swift side. Without + * the balancing deinit(pointer) call on cache hit, each crossing leaks +1 retain + * and the object is never deallocated. + * + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testRetainLeakOnCacheHit(exports) { + exports.resetRetainLeakDeinits(); + exports.resetRetainLeakSubject(); + + const wrappers = []; + for (let i = 0; i < 10; i++) { + wrappers.push(exports.getRetainLeakSubject()); + } + + for (let i = 1; i < wrappers.length; i++) { + assert.strictEqual( + wrappers[0], + wrappers[i], + "All should be the same cached wrapper", + ); + } + + wrappers[0].release(); + exports.resetRetainLeakSubject(); + + assert.strictEqual( + exports.getRetainLeakDeinits(), + 1, + "Object should be deallocated after release + reset. " + + "If deinits == 0, retain leak from unbalanced passRetained on cache hits.", + ); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testArrayElementIdentity(exports) { + exports.setupArrayPool(10); + const arr1 = exports.getArrayPool(); + const arr2 = exports.getArrayPool(); + + assert.equal(arr1.length, 10); + assert.equal(arr2.length, 10); + + for (let i = 0; i < 10; i++) { + assert.strictEqual( + arr1[i], + arr2[i], + `Array element at index ${i} should be === across calls`, + ); + assert.equal(arr1[i].tag, i); + } + + for (const elem of arr1) { + elem.release(); + } + exports.clearArrayPool(); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testArrayElementMatchesSingleGetter(exports) { + exports.setupArrayPool(5); + const arr = exports.getArrayPool(); + const single = exports.getArrayPoolElement(2); + + assert.strictEqual( + arr[2], + single, + "Array element and single getter should return the same wrapper", + ); + assert.equal(single.tag, 2); + + for (const elem of arr) { + elem.release(); + } + exports.clearArrayPool(); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testArrayRetainLeak(exports) { + exports.resetArrayPoolDeinits(); + exports.setupArrayPool(5); + + for (let round = 0; round < 10; round++) { + exports.getArrayPool(); + } + + const arr = exports.getArrayPool(); + for (const elem of arr) { + elem.release(); + } + + exports.clearArrayPool(); + + assert.strictEqual( + exports.getArrayPoolDeinits(), + 5, + "All 5 pool objects should be deallocated after release + clear. " + + "If deinits < 5, retain leak from unbalanced passRetained in array returns.", + ); +} + +/** + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports + */ +function testDifferentClassesDontCollide(exports) { + const subject1 = new exports.IdentityTestSubject(1); + const subject2 = new exports.IdentityTestSubject(2); + + assert.notStrictEqual( + subject1, + subject2, + "Different instances should not be ===", + ); + assert.equal(subject1.currentValue, 1); + assert.equal(subject2.currentValue, 2); + + subject1.release(); + subject2.release(); +} diff --git a/Tests/BridgeJSIdentityTests/bridge-js.config.json b/Tests/BridgeJSIdentityTests/bridge-js.config.json new file mode 100644 index 000000000..29884404e --- /dev/null +++ b/Tests/BridgeJSIdentityTests/bridge-js.config.json @@ -0,0 +1,3 @@ +{ + "identityMode": "pointer" +} diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift new file mode 100644 index 000000000..041f251f4 --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -0,0 +1,85 @@ +import Testing +import JavaScriptKit + +@JSClass struct AsyncImportImports { + @JSFunction static func jsAsyncRoundTripVoid() async throws(JSException) + @JSFunction static func jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double + @JSFunction static func jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool + @JSFunction static func jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String + @JSFunction static func jsAsyncRoundTripOptionalString(_ v: String?) async throws(JSException) -> String? + @JSFunction static func jsAsyncRoundTripOptionalNumber(_ v: Double?) async throws(JSException) -> Double? + @JSFunction static func jsAsyncRoundTripBoolArray(_ values: [Bool]) async throws(JSException) -> [Bool] + @JSFunction static func jsAsyncRoundTripIntArray(_ values: [Double]) async throws(JSException) -> [Double] + @JSFunction static func jsAsyncRoundTripStringArray(_ values: [String]) async throws(JSException) -> [String] + @JSFunction static func jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag +} + +@Suite struct AsyncImportTests { + @Test func asyncRoundTripVoid() async throws { + try await AsyncImportImports.jsAsyncRoundTripVoid() + } + + @Test(arguments: [0.0, 1.0, -1.0, Double.pi, Double.infinity]) + func asyncRoundTripNumber(v: Double) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripNumber(v) == v) + } + + @Test(arguments: [true, false]) + func asyncRoundTripBool(v: Bool) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripBool(v) == v) + } + + @Test(arguments: ["", "Hello, world!", "🧑‍🧑‍🧒"]) + func asyncRoundTripString(v: String) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripString(v) == v) + } + + // MARK: - Stack ABI types + + @Test(arguments: ["hello" as String?, nil, "🧑‍🧑‍🧒" as String?]) + func asyncRoundTripOptionalString(v: String?) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripOptionalString(v) == v) + } + + @Test(arguments: [42.0 as Double?, nil, 0.0 as Double?]) + func asyncRoundTripOptionalNumber(v: Double?) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripOptionalNumber(v) == v) + } + + @Test func asyncRoundTripBoolArray() async throws { + let values: [Bool] = [true, false, true] + try #expect(await AsyncImportImports.jsAsyncRoundTripBoolArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripBoolArray([]) == []) + } + + @Test func asyncRoundTripIntArray() async throws { + let values: [Double] = [1, 2, 3, 4, 5] + try #expect(await AsyncImportImports.jsAsyncRoundTripIntArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripIntArray([]) == []) + } + + @Test func asyncRoundTripStringArray() async throws { + let values = ["Hello", "World", "🎉"] + try #expect(await AsyncImportImports.jsAsyncRoundTripStringArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripStringArray([]) == []) + } + + @Test(arguments: [FeatureFlag.foo, .bar]) + func asyncRoundTripFeatureFlag(v: FeatureFlag) async throws { + try #expect(await AsyncImportImports.jsAsyncRoundTripFeatureFlag(v) == v) + } + + // MARK: - Structured return type + + @Test func fetchWeatherData() async throws { + let weather = try await BridgeJSRuntimeTests.fetchWeatherData("London") + #expect(try weather.temperature == 15.5) + #expect(try weather.description == "Cloudy") + #expect(try weather.humidity == 80) + + let weather2 = try await BridgeJSRuntimeTests.fetchWeatherData("Tokyo") + #expect(try weather2.temperature == 25.0) + #expect(try weather2.description == "Sunny") + #expect(try weather2.humidity == 40) + } +} diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 171d0dd3a..c6e216203 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -207,7 +207,7 @@ extension Greeter { // MARK: - Enum Tests -@JS enum Direction { +@JS public enum Direction { case north case south case east @@ -433,6 +433,12 @@ class UUID { @JS func uuidString() -> String { return value } + + @JS static func fromValue(_ value: String) -> UUID { + return UUID(value: value) + } + + @JS static var placeholder: String { "00000000-0000-0000-0000-000000000000" } } @JS func createUUID(value: String) -> UUID { @@ -1279,6 +1285,36 @@ enum GraphOperations { processor.increment(by: 7) return callback(processor) + " | " + callback(nil) } + + @JS func processVector(_ callback: (Double) -> Vector2D) -> Double { + return callback(3.0).magnitude() + } + + @JS func processOptionalVector(_ callback: (Double) -> Vector2D?) -> String { + let some = callback(2.0) + let none = callback(-1.0) + let someStr = some.map { "(\($0.dx),\($0.dy))" } ?? "nil" + let noneStr = none.map { "(\($0.dx),\($0.dy))" } ?? "nil" + return "\(someStr) | \(noneStr)" + } +} + +@JS enum NestedStructGroupA { + @JS struct Metadata { + var label: String + var count: Int + } + + @JS static func roundtripMetadata(_ m: Metadata) -> Metadata { m } +} + +@JS enum NestedStructGroupB { + @JS struct Metadata { + var tag: String + var value: Double + } + + @JS static func roundtripMetadata(_ m: Metadata) -> Metadata { m } } class ExportAPITests: XCTestCase { @@ -1301,6 +1337,6 @@ class ExportAPITests: XCTestCase { } func testAllAsync() async throws { - _ = try await runAsyncWorks().value() + try await runAsyncWorks() } } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index ac9ad0bc6..08d0db2a7 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -1,3 +1,4 @@ +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // @@ -41,7 +42,18 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSFunction func changeName(_ name: String) throws(JSException) -> Void } -@JSFunction func runAsyncWorks() throws(JSException) -> JSPromise +@JSFunction func runAsyncWorks() async throws(JSException) -> Void + +@JSFunction func fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData + +@JSClass struct WeatherData { + @JSGetter var temperature: Double + @JSSetter func setTemperature(_ value: Double) throws(JSException) + @JSGetter var description: String + @JSSetter func setDescription(_ value: String) throws(JSException) + @JSGetter var humidity: Double + @JSSetter func setHumidity(_ value: Double) throws(JSException) +} @JSFunction(jsName: "$jsWeirdFunction") func _jsWeirdFunction() throws(JSException) -> Double diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index b54106be9..e6c2f940b 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -1,4 +1,5 @@ // bridge-js: skip +// swift-format-ignore-file // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. // @@ -369,7 +370,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTests7GreeterC_SS { } extension JSTypedClosure where Signature == (Greeter) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Greeter) -> String) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Greeter) -> String) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7GreeterC_SS, body: body, @@ -687,7 +688,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_7GreeterC { } extension JSTypedClosure where Signature == (String) -> Greeter { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Greeter) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Greeter) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_7GreeterC, body: body, @@ -753,7 +754,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_SS { } extension JSTypedClosure where Signature == (String) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> String) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> String) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_SS, body: body, @@ -775,6 +776,69 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_ #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSd_8Vector2DV { + static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Vector2D { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV(callbackValue, param0Value) + return Vector2D.bridgeJSLiftReturn() + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Double) -> Vector2D { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Vector2D) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_8Vector2DV(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Vector2D>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Double.bridgeJSLiftParameter(param0)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd_extern(_ callback: Int32, _ param0: Float64) -> Float64 @@ -838,6 +902,69 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_ #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSd_Sq8Vector2DV { + static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Optional { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV(callbackValue, param0Value) + return Optional.bridgeJSLiftReturn() + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Double) -> Optional { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Optional) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sq8Vector2DV(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Double.bridgeJSLiftParameter(param0)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS_extern(_ callback: Int32, _ param0: Int32, _ param1Bytes: Int32, _ param1Length: Int32, _ param2: Float64) -> Int32 @@ -1228,41 +1355,730 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq1 } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(callback, param0IsSome, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq5ThemeO_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let ret0 = param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(callbackValue, param0IsSome, param0Bytes, param0Length) + return ret + } + let ret = ret0 + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(callback, param0IsSome, param0Pointer) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Pointer) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(callbackValue, param0IsSome, param0Pointer) + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(callback, param0IsSome, param0Pointer) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> Optional { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Pointer) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(callbackValue, param0IsSome, param0Pointer) + return Optional.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> Optional { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> Optional) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(callback, param0IsSome, param0CaseId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9APIResultO_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0CaseId) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(callbackValue, param0IsSome, param0CaseId) + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0CaseId)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(callback, param0IsSome, param0Value) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9DirectionO_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Value) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(callbackValue, param0IsSome, param0Value) + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(callback, param0IsSome, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSS_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let ret0 = param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(callbackValue, param0IsSome, param0Bytes, param0Length) + return ret + } + let ret = ret0 + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(callback, param0IsSome, param0Value) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSi_SS { + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Value) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(callbackValue, param0IsSome, param0Value) + return String.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Optional) -> String { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss11FeatureFlagO_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending FeatureFlag) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending FeatureFlag) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending FeatureFlag) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending FeatureFlag) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(FeatureFlag.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss11WeatherDataC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending WeatherData) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending WeatherData) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending WeatherData) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending WeatherData) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(WeatherData.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending String) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending String) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ callback: Int32) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ callback: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(callback, param0IsSome, param0Bytes, param0Length) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(callback) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSq5ThemeO_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [String]) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let ret0 = param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(callbackValue, param0IsSome, param0Bytes, param0Length) - return ret - } - let ret = ret0 - return String.bridgeJSLiftReturn(ret) + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(callbackValue) #else fatalError("Only available on WebAssembly") #endif @@ -1270,10 +2086,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSq5ThemeO_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending [String]) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending [String]) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y, body: body, fileID: fileID, line: line @@ -1281,51 +2097,49 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq5ThemeO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [String]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([String].bridgeJSLiftParameter()) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ callback: Int32) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ callback: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(callback, param0IsSome, param0Pointer) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(callback) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [Bool]) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let (param0IsSome, param0Pointer) = param0.bridgeJSLowerParameter() - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(callbackValue, param0IsSome, param0Pointer) - return String.bridgeJSLiftReturn(ret) + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(callbackValue) #else fatalError("Only available on WebAssembly") #endif @@ -1333,10 +2147,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending [Bool]) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending [Bool]) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y, body: body, fileID: fileID, line: line @@ -1344,51 +2158,49 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: UnsafeMutableRawPointer) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [Bool]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([Bool].bridgeJSLiftParameter()) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ callback: Int32) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ callback: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(callback, param0IsSome, param0Pointer) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(callback) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> Optional { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [Double]) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let (param0IsSome, param0Pointer) = param0.bridgeJSLowerParameter() - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(callbackValue, param0IsSome, param0Pointer) - return Optional.bridgeJSLiftReturn(ret) + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(callbackValue) #else fatalError("Only available on WebAssembly") #endif @@ -1396,10 +2208,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC { } } -extension JSTypedClosure where Signature == (Optional) -> Optional { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> Optional) { +extension JSTypedClosure where Signature == (sending [Double]) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending [Double]) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y, body: body, fileID: fileID, line: line @@ -1407,51 +2219,49 @@ extension JSTypedClosure where Signature == (Optional) -> Optional Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [Double]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([Double].bridgeJSLiftParameter()) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(callback, param0IsSome, param0CaseId) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9APIResultO_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Bool) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let (param0IsSome, param0CaseId) = param0.bridgeJSLowerParameter() - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(callbackValue, param0IsSome, param0CaseId) - return String.bridgeJSLiftReturn(ret) + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -1459,10 +2269,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9APIResultO_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Bool) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y, body: body, fileID: fileID, line: line @@ -1470,51 +2280,49 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0CaseId)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Bool.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(callback, param0IsSome, param0Value) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9DirectionO_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let (param0IsSome, param0Value) = param0.bridgeJSLowerParameter() - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(callbackValue, param0IsSome, param0Value) - return String.bridgeJSLiftReturn(ret) + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -1522,10 +2330,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSq9DirectionO_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y, body: body, fileID: fileID, line: line @@ -1533,54 +2341,50 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9DirectionO_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(callback, param0IsSome, param0Bytes, param0Length) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(callback, param0IsSome, param0Bytes, param0Length) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSS_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSqSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Optional) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() - let ret0 = param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(callbackValue, param0IsSome, param0Bytes, param0Length) - return ret + param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(callbackValue, param0IsSome, param0Bytes, param0Length) } - let ret = ret0 - return String.bridgeJSLiftReturn(ret) #else fatalError("Only available on WebAssembly") #endif @@ -1588,10 +2392,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSS_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending Optional) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Optional) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y, body: body, fileID: fileID, line: line @@ -1599,51 +2403,49 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSS_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Optional) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) #else fatalError("Only available on WebAssembly") #endif } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void #else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Int32) -> Int32 { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(callback, param0IsSome, param0Value) +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(callback, param0IsSome, param0Value) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSi_SS { - static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> String { +private enum _BJS_Closure_20BridgeJSRuntimeTestssSqSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Optional) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let (param0IsSome, param0Value) = param0.bridgeJSLowerParameter() - let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(callbackValue, param0IsSome, param0Value) - return String.bridgeJSLiftReturn(ret) + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(callbackValue, param0IsSome, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -1651,10 +2453,10 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSqSi_SS { } } -extension JSTypedClosure where Signature == (Optional) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Optional) -> String) { +extension JSTypedClosure where Signature == (sending Optional) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Optional) -> Void) { self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS, + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y, body: body, fileID: fileID, line: line @@ -1662,13 +2464,12 @@ extension JSTypedClosure where Signature == (Optional) -> String { } } -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqSi_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) - return result.bridgeJSLowerReturn() + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Optional) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) #else fatalError("Only available on WebAssembly") #endif @@ -2746,7 +3547,8 @@ public func _bjs_ArraySupportExports_static_roundTripProtocolArray() -> Void { #if arch(wasm32) let ret = ArraySupportExports.roundTripProtocolArray(_: [AnyArrayElementProtocol].bridgeJSStackPop()) for __bjs_elem_ret in ret { - _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())} + _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } _swift_js_push_i32(Int32(ret.count)) #else fatalError("Only available on WebAssembly") @@ -3193,7 +3995,7 @@ extension Direction: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -3208,7 +4010,7 @@ extension Direction: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -3236,7 +4038,7 @@ extension Status: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .loading @@ -3249,7 +4051,7 @@ extension Status: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .loading: return 0 @@ -3293,7 +4095,7 @@ extension TSDirection: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .north @@ -3308,7 +4110,7 @@ extension TSDirection: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .north: return 0 @@ -3361,7 +4163,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -3376,7 +4178,7 @@ extension Networking.API.Method: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 @@ -3410,7 +4212,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .get @@ -3421,7 +4223,7 @@ extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .get: return 0 @@ -3694,7 +4496,7 @@ extension StaticCalculator: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .scientific @@ -3705,7 +4507,7 @@ extension StaticCalculator: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .scientific: return 0 @@ -3795,7 +4597,7 @@ extension StaticPropertyEnum: _BridgedSwiftCaseEnum { return bridgeJSLowerParameter() } - private init?(bridgeJSRawValue: Int32) { + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { switch bridgeJSRawValue { case 0: self = .option1 @@ -3806,7 +4608,7 @@ extension StaticPropertyEnum: _BridgedSwiftCaseEnum { } } - private var bridgeJSRawValue: Int32 { + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { switch self { case .option1: return 0 @@ -4007,6 +4809,67 @@ public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_se #endif } +@_expose(wasm, "bjs_NestedStructGroupA_static_roundtripMetadata") +@_cdecl("bjs_NestedStructGroupA_static_roundtripMetadata") +public func _bjs_NestedStructGroupA_static_roundtripMetadata() -> Void { + #if arch(wasm32) + let ret = NestedStructGroupA.roundtripMetadata(_: NestedStructGroupA.Metadata.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_NestedStructGroupB_static_roundtripMetadata") +@_cdecl("bjs_NestedStructGroupB_static_roundtripMetadata") +public func _bjs_NestedStructGroupB_static_roundtripMetadata() -> Void { + #if arch(wasm32) + let ret = NestedStructGroupB.roundtripMetadata(_: NestedStructGroupB.Metadata.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension LightColor: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> LightColor { + return bridgeJSLiftParameter(value) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> LightColor { + return LightColor(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSLowerParameter() + } + + @_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .red + case 1: + self = .yellow + case 2: + self = .green + default: + return nil + } + } + + @_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 { + switch self { + case .red: + return 0 + case .yellow: + return 1 + case .green: + return 2 + } + } +} + @_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripInt") @_cdecl("bjs_IntegerTypesSupportExports_static_roundTripInt") public func _bjs_IntegerTypesSupportExports_static_roundTripInt(_ v: Int32) -> Int32 { @@ -4077,40 +4940,84 @@ public func _bjs_IntegerTypesSupportExports_static_roundTripUInt16(_ v: Int32) - @_cdecl("bjs_IntegerTypesSupportExports_static_roundTripInt32") public func _bjs_IntegerTypesSupportExports_static_roundTripInt32(_ v: Int32) -> Int32 { #if arch(wasm32) - let ret = IntegerTypesSupportExports.roundTripInt32(_: Int32.bridgeJSLiftParameter(v)) + let ret = IntegerTypesSupportExports.roundTripInt32(_: Int32.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripUInt32") +@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripUInt32") +public func _bjs_IntegerTypesSupportExports_static_roundTripUInt32(_ v: Int32) -> Int32 { + #if arch(wasm32) + let ret = IntegerTypesSupportExports.roundTripUInt32(_: UInt32.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripInt64") +@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripInt64") +public func _bjs_IntegerTypesSupportExports_static_roundTripInt64(_ v: Int64) -> Int64 { + #if arch(wasm32) + let ret = IntegerTypesSupportExports.roundTripInt64(_: Int64.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripUInt64") +@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripUInt64") +public func _bjs_IntegerTypesSupportExports_static_roundTripUInt64(_ v: Int64) -> Int64 { + #if arch(wasm32) + let ret = IntegerTypesSupportExports.roundTripUInt64(_: UInt64.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_JSTypedArrayExports_static_roundTripUint8Array") +@_cdecl("bjs_JSTypedArrayExports_static_roundTripUint8Array") +public func _bjs_JSTypedArrayExports_static_roundTripUint8Array(_ v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSTypedArrayExports.roundTripUint8Array(_: JSUint8Array.bridgeJSLiftParameter(v)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") #endif } -@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripUInt32") -@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripUInt32") -public func _bjs_IntegerTypesSupportExports_static_roundTripUInt32(_ v: Int32) -> Int32 { +@_expose(wasm, "bjs_JSTypedArrayExports_static_roundTripFloat32Array") +@_cdecl("bjs_JSTypedArrayExports_static_roundTripFloat32Array") +public func _bjs_JSTypedArrayExports_static_roundTripFloat32Array(_ v: Int32) -> Int32 { #if arch(wasm32) - let ret = IntegerTypesSupportExports.roundTripUInt32(_: UInt32.bridgeJSLiftParameter(v)) + let ret = JSTypedArrayExports.roundTripFloat32Array(_: JSFloat32Array.bridgeJSLiftParameter(v)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") #endif } -@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripInt64") -@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripInt64") -public func _bjs_IntegerTypesSupportExports_static_roundTripInt64(_ v: Int64) -> Int64 { +@_expose(wasm, "bjs_JSTypedArrayExports_static_roundTripFloat64Array") +@_cdecl("bjs_JSTypedArrayExports_static_roundTripFloat64Array") +public func _bjs_JSTypedArrayExports_static_roundTripFloat64Array(_ v: Int32) -> Int32 { #if arch(wasm32) - let ret = IntegerTypesSupportExports.roundTripInt64(_: Int64.bridgeJSLiftParameter(v)) + let ret = JSTypedArrayExports.roundTripFloat64Array(_: JSFloat64Array.bridgeJSLiftParameter(v)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") #endif } -@_expose(wasm, "bjs_IntegerTypesSupportExports_static_roundTripUInt64") -@_cdecl("bjs_IntegerTypesSupportExports_static_roundTripUInt64") -public func _bjs_IntegerTypesSupportExports_static_roundTripUInt64(_ v: Int64) -> Int64 { +@_expose(wasm, "bjs_JSTypedArrayExports_static_roundTripInt32Array") +@_cdecl("bjs_JSTypedArrayExports_static_roundTripInt32Array") +public func _bjs_JSTypedArrayExports_static_roundTripInt32Array(_ v: Int32) -> Int32 { #if arch(wasm32) - let ret = IntegerTypesSupportExports.roundTripUInt64(_: UInt64.bridgeJSLiftParameter(v)) + let ret = JSTypedArrayExports.roundTripInt32Array(_: JSInt32Array.bridgeJSLiftParameter(v)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -4517,6 +5424,102 @@ extension APIOptionalResult: _BridgedSwiftAssociatedValueEnum { } } +extension NestedStructGroupA.Metadata: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> NestedStructGroupA.Metadata { + let count = Int.bridgeJSStackPop() + let label = String.bridgeJSStackPop() + return NestedStructGroupA.Metadata(label: label, count: count) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.label.bridgeJSStackPush() + self.count.bridgeJSStackPush() + } + + init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_NestedStructGroupA_Metadata(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_NestedStructGroupA_Metadata())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_NestedStructGroupA_Metadata") +fileprivate func _bjs_struct_lower_NestedStructGroupA_Metadata_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_NestedStructGroupA_Metadata_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_NestedStructGroupA_Metadata(_ objectId: Int32) -> Void { + return _bjs_struct_lower_NestedStructGroupA_Metadata_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_NestedStructGroupA_Metadata") +fileprivate func _bjs_struct_lift_NestedStructGroupA_Metadata_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_NestedStructGroupA_Metadata_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_NestedStructGroupA_Metadata() -> Int32 { + return _bjs_struct_lift_NestedStructGroupA_Metadata_extern() +} + +extension NestedStructGroupB.Metadata: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> NestedStructGroupB.Metadata { + let value = Double.bridgeJSStackPop() + let tag = String.bridgeJSStackPop() + return NestedStructGroupB.Metadata(tag: tag, value: value) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSStackPush() { + self.tag.bridgeJSStackPush() + self.value.bridgeJSStackPush() + } + + init(unsafelyCopying jsObject: JSObject) { + _bjs_struct_lower_NestedStructGroupB_Metadata(jsObject.bridgeJSLowerParameter()) + self = Self.bridgeJSStackPop() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSStackPush() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_NestedStructGroupB_Metadata())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_NestedStructGroupB_Metadata") +fileprivate func _bjs_struct_lower_NestedStructGroupB_Metadata_extern(_ objectId: Int32) -> Void +#else +fileprivate func _bjs_struct_lower_NestedStructGroupB_Metadata_extern(_ objectId: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lower_NestedStructGroupB_Metadata(_ objectId: Int32) -> Void { + return _bjs_struct_lower_NestedStructGroupB_Metadata_extern(objectId) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_NestedStructGroupB_Metadata") +fileprivate func _bjs_struct_lift_NestedStructGroupB_Metadata_extern() -> Int32 +#else +fileprivate func _bjs_struct_lift_NestedStructGroupB_Metadata_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func _bjs_struct_lift_NestedStructGroupB_Metadata() -> Int32 { + return _bjs_struct_lift_NestedStructGroupB_Metadata_extern() +} + extension Point: _BridgedSwiftStruct { @_spi(BridgeJS) @_transparent public static func bridgeJSStackPop() -> Point { let y = Int.bridgeJSStackPop() @@ -7928,6 +8931,28 @@ public func _bjs___Swift_Foundation_UUID_uuidString(_ _self: UnsafeMutableRawPoi #endif } +@_expose(wasm, "bjs___Swift_Foundation_UUID_static_fromValue") +@_cdecl("bjs___Swift_Foundation_UUID_static_fromValue") +public func _bjs___Swift_Foundation_UUID_static_fromValue(_ valueBytes: Int32, _ valueLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = UUID.fromValue(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs___Swift_Foundation_UUID_static_placeholder_get") +@_cdecl("bjs___Swift_Foundation_UUID_static_placeholder_get") +public func _bjs___Swift_Foundation_UUID_static_placeholder_get() -> Void { + #if arch(wasm32) + let ret = UUID.placeholder + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs___Swift_Foundation_UUID_deinit") @_cdecl("bjs___Swift_Foundation_UUID_deinit") public func _bjs___Swift_Foundation_UUID_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { @@ -9415,7 +10440,8 @@ public func _bjs_ProtocolReturnTests_static_createNativeProcessorArray() -> Void #if arch(wasm32) let ret = ProtocolReturnTests.createNativeProcessorArray() for __bjs_elem_ret in ret { - _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())} + _swift_js_push_i32((__bjs_elem_ret as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } _swift_js_push_i32(Int32(ret.count)) #else fatalError("Only available on WebAssembly") @@ -9428,8 +10454,9 @@ public func _bjs_ProtocolReturnTests_static_createNativeProcessorDictionary() -> #if arch(wasm32) let ret = ProtocolReturnTests.createNativeProcessorDictionary() for __bjs_kv_ret in ret { - __bjs_kv_ret.key.bridgeJSStackPush() - _swift_js_push_i32((__bjs_kv_ret.value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn())} + __bjs_kv_ret.key.bridgeJSStackPush() + _swift_js_push_i32((__bjs_kv_ret.value as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()) + } _swift_js_push_i32(Int32(ret.count)) #else fatalError("Only available on WebAssembly") @@ -9742,6 +10769,28 @@ public func _bjs_TextProcessor_processOptionalDataProcessor(_ _self: UnsafeMutab #endif } +@_expose(wasm, "bjs_TextProcessor_processVector") +@_cdecl("bjs_TextProcessor_processVector") +public func _bjs_TextProcessor_processVector(_ _self: UnsafeMutableRawPointer, _ callback: Int32) -> Float64 { + #if arch(wasm32) + let ret = TextProcessor.bridgeJSLiftParameter(_self).processVector(_: _BJS_Closure_20BridgeJSRuntimeTestsSd_8Vector2DV.bridgeJSLift(callback)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_TextProcessor_processOptionalVector") +@_cdecl("bjs_TextProcessor_processOptionalVector") +public func _bjs_TextProcessor_processOptionalVector(_ _self: UnsafeMutableRawPointer, _ callback: Int32) -> Void { + #if arch(wasm32) + let ret = TextProcessor.bridgeJSLiftParameter(_self).processOptionalVector(_: _BJS_Closure_20BridgeJSRuntimeTestsSd_Sq8Vector2DV.bridgeJSLift(callback)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_TextProcessor_deinit") @_cdecl("bjs_TextProcessor_deinit") public func _bjs_TextProcessor_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { @@ -10408,85 +11457,326 @@ func _$ArraySupportImports_jsRoundTripJSClassArray(_ values: [ArrayElementObject if let error = _swift_js_take_exception() { throw error } - return [ArrayElementObject].bridgeJSLiftReturn() + return [ArrayElementObject].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalIntArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalIntArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalStringArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalStringArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalBoolArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalBoolArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalJSValueArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalJSValueArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalJSObjectArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalJSObjectArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsRoundTripOptionalJSClassArray(_ values: [Optional]) throws(JSException) -> [Optional] { + let _ = values.bridgeJSLowerParameter() + bjs_ArraySupportImports_jsRoundTripOptionalJSClassArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Optional].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double { + let _ = values.bridgeJSLowerParameter() + let ret = bjs_ArraySupportImports_jsSumNumberArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +func _$ArraySupportImports_jsCreateNumberArray() throws(JSException) -> [Double] { + bjs_ArraySupportImports_jsCreateNumberArray_static() + if let error = _swift_js_take_exception() { + throw error + } + return [Double].bridgeJSLiftReturn() +} + +func _$ArraySupportImports_runJsArraySupportTests() throws(JSException) -> Void { + bjs_ArraySupportImports_runJsArraySupportTests_static() + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripVoid_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripNumber_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripBool_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripString_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(resolveRef, rejectRef, vBytes, vLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(resolveRef, rejectRef, vIsSome, vBytes, vLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(resolveRef, rejectRef, vIsSome, vValue) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(resolveRef, rejectRef, vBytes, vLength) +} + +func _$AsyncImportImports_jsAsyncRoundTripVoid() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_AsyncImportImports_jsAsyncRoundTripVoid_static(resolveRef, rejectRef) + } } -func _$ArraySupportImports_jsRoundTripOptionalIntArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalIntArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripNumber_static(resolveRef, rejectRef, vValue) } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsRoundTripOptionalStringArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalStringArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Bool) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripBool_static(resolveRef, rejectRef, vValue) } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsRoundTripOptionalBoolArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalBoolArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending String) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripString_static(resolveRef, rejectRef, vBytes, vLength) + } } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsRoundTripOptionalJSValueArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalJSValueArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vIsSome, vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static(resolveRef, rejectRef, vIsSome, vBytes, vLength) + } } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsRoundTripOptionalJSObjectArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalJSObjectArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let (vIsSome, vValue) = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static(resolveRef, rejectRef, vIsSome, vValue) } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsRoundTripOptionalJSClassArray(_ values: [Optional]) throws(JSException) -> [Optional] { - let _ = values.bridgeJSLowerParameter() - bjs_ArraySupportImports_jsRoundTripOptionalJSClassArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripBoolArray(_ values: [Bool]) async throws(JSException) -> [Bool] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Bool]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static(resolveRef, rejectRef) } - return [Optional].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double { - let _ = values.bridgeJSLowerParameter() - let ret = bjs_ArraySupportImports_jsSumNumberArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripIntArray(_ values: [Double]) async throws(JSException) -> [Double] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Double]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static(resolveRef, rejectRef) } - return Double.bridgeJSLiftReturn(ret) + return resolved } -func _$ArraySupportImports_jsCreateNumberArray() throws(JSException) -> [Double] { - bjs_ArraySupportImports_jsCreateNumberArray_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripStringArray(_ values: [String]) async throws(JSException) -> [String] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [String]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static(resolveRef, rejectRef) } - return [Double].bridgeJSLiftReturn() + return resolved } -func _$ArraySupportImports_runJsArraySupportTests() throws(JSException) -> Void { - bjs_ArraySupportImports_runJsArraySupportTests_static() - if let error = _swift_js_take_exception() { - throw error +func _$AsyncImportImports_jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending FeatureFlag) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static(resolveRef, rejectRef, vBytes, vLength) + } } + return resolved } #if arch(wasm32) @@ -11296,22 +12586,49 @@ func _$jsRoundTripFeatureFlag(_ flag: FeatureFlag) throws(JSException) -> Featur #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_runAsyncWorks") -fileprivate func bjs_runAsyncWorks_extern() -> Int32 +fileprivate func bjs_runAsyncWorks_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void #else -fileprivate func bjs_runAsyncWorks_extern() -> Int32 { +fileprivate func bjs_runAsyncWorks_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_runAsyncWorks() -> Int32 { - return bjs_runAsyncWorks_extern() +@inline(never) fileprivate func bjs_runAsyncWorks(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_runAsyncWorks_extern(resolveRef, rejectRef) } -func _$runAsyncWorks() throws(JSException) -> JSPromise { - let ret = bjs_runAsyncWorks() - if let error = _swift_js_take_exception() { - throw error +func _$runAsyncWorks() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_runAsyncWorks(resolveRef, rejectRef) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void +#else +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_fetchWeatherData(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + return bjs_fetchWeatherData_extern(resolveRef, rejectRef, cityBytes, cityLength) +} + +func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending WeatherData) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in + bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) + } } - return JSPromise.bridgeJSLiftReturn(ret) + return resolved } #if arch(wasm32) @@ -11492,6 +12809,133 @@ func _$JsGreeter_changeName(_ self: JSObject, _ name: String) throws(JSException } } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_temperature_get") +fileprivate func bjs_WeatherData_temperature_get_extern(_ self: Int32) -> Float64 +#else +fileprivate func bjs_WeatherData_temperature_get_extern(_ self: Int32) -> Float64 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_temperature_get(_ self: Int32) -> Float64 { + return bjs_WeatherData_temperature_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_description_get") +fileprivate func bjs_WeatherData_description_get_extern(_ self: Int32) -> Int32 +#else +fileprivate func bjs_WeatherData_description_get_extern(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_description_get(_ self: Int32) -> Int32 { + return bjs_WeatherData_description_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_humidity_get") +fileprivate func bjs_WeatherData_humidity_get_extern(_ self: Int32) -> Float64 +#else +fileprivate func bjs_WeatherData_humidity_get_extern(_ self: Int32) -> Float64 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_humidity_get(_ self: Int32) -> Float64 { + return bjs_WeatherData_humidity_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_temperature_set") +fileprivate func bjs_WeatherData_temperature_set_extern(_ self: Int32, _ newValue: Float64) -> Void +#else +fileprivate func bjs_WeatherData_temperature_set_extern(_ self: Int32, _ newValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_temperature_set(_ self: Int32, _ newValue: Float64) -> Void { + return bjs_WeatherData_temperature_set_extern(self, newValue) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_description_set") +fileprivate func bjs_WeatherData_description_set_extern(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void +#else +fileprivate func bjs_WeatherData_description_set_extern(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_description_set(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void { + return bjs_WeatherData_description_set_extern(self, newValueBytes, newValueLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_humidity_set") +fileprivate func bjs_WeatherData_humidity_set_extern(_ self: Int32, _ newValue: Float64) -> Void +#else +fileprivate func bjs_WeatherData_humidity_set_extern(_ self: Int32, _ newValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_humidity_set(_ self: Int32, _ newValue: Float64) -> Void { + return bjs_WeatherData_humidity_set_extern(self, newValue) +} + +func _$WeatherData_temperature_get(_ self: JSObject) throws(JSException) -> Double { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_temperature_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_description_get(_ self: JSObject) throws(JSException) -> String { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_description_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return String.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_humidity_get(_ self: JSObject) throws(JSException) -> Double { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_humidity_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_temperature_set(_ self: JSObject, _ newValue: Double) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let newValueValue = newValue.bridgeJSLowerParameter() + bjs_WeatherData_temperature_set(selfValue, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WeatherData_description_set(_ self: JSObject, _ newValue: String) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + newValue.bridgeJSWithLoweredParameter { (newValueBytes, newValueLength) in + bjs_WeatherData_description_set(selfValue, newValueBytes, newValueLength) + } + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WeatherData_humidity_set(_ self: JSObject, _ newValue: Double) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let newValueValue = newValue.bridgeJSLowerParameter() + bjs_WeatherData_humidity_set(selfValue, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__WeirdClass_init") fileprivate func bjs__WeirdClass_init_extern() -> Int32 @@ -11851,6 +13295,27 @@ func _$Animal_getIsCat(_ self: JSObject) throws(JSException) -> Bool { return Bool.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripLightColor") +fileprivate func bjs_jsRoundTripLightColor_extern(_ value: Int32) -> Int32 +#else +fileprivate func bjs_jsRoundTripLightColor_extern(_ value: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsRoundTripLightColor(_ value: Int32) -> Int32 { + return bjs_jsRoundTripLightColor_extern(value) +} + +func _$jsRoundTripLightColor(_ value: LightColor) throws(JSException) -> LightColor { + let valueValue = value.bridgeJSLowerParameter() + let ret = bjs_jsRoundTripLightColor(valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return LightColor.bridgeJSLiftReturn(ret) +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsTranslatePoint") fileprivate func bjs_jsTranslatePoint_extern(_ point: Int32, _ dx: Int32, _ dy: Int32) -> Int32 @@ -11874,6 +13339,27 @@ func _$jsTranslatePoint(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException return Point.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalPoint") +fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void +#else +fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsRoundTripOptionalPoint(_ point: Int32) -> Void { + return bjs_jsRoundTripOptionalPoint_extern(point) +} + +func _$jsRoundTripOptionalPoint(_ point: Optional) throws(JSException) -> Optional { + let pointIsSome = point.bridgeJSLowerParameter() + bjs_jsRoundTripOptionalPoint(pointIsSome) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn() +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_IntegerTypesSupportImports_jsRoundTripInt_static") fileprivate func bjs_IntegerTypesSupportImports_jsRoundTripInt_static_extern(_ v: Int32) -> Int32 @@ -12297,6 +13783,129 @@ func _$JSClassSupportImports_makeJSClassWithArrayMembers(_ numbers: [Int], _ lab return JSClassWithArrayMembers.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_jsCreateUint8Array_static") +fileprivate func bjs_JSTypedArrayImports_jsCreateUint8Array_static_extern() -> Int32 +#else +fileprivate func bjs_JSTypedArrayImports_jsCreateUint8Array_static_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_jsCreateUint8Array_static() -> Int32 { + return bjs_JSTypedArrayImports_jsCreateUint8Array_static_extern() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_jsRoundTripUint8Array_static") +fileprivate func bjs_JSTypedArrayImports_jsRoundTripUint8Array_static_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_JSTypedArrayImports_jsRoundTripUint8Array_static_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_jsRoundTripUint8Array_static(_ v: Int32) -> Int32 { + return bjs_JSTypedArrayImports_jsRoundTripUint8Array_static_extern(v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static") +fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static(_ v: Int32) -> Int32 { + return bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static_extern(v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static") +fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static(_ v: Int32) -> Int32 { + return bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static_extern(v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_jsRoundTripInt32Array_static") +fileprivate func bjs_JSTypedArrayImports_jsRoundTripInt32Array_static_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_JSTypedArrayImports_jsRoundTripInt32Array_static_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_jsRoundTripInt32Array_static(_ v: Int32) -> Int32 { + return bjs_JSTypedArrayImports_jsRoundTripInt32Array_static_extern(v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JSTypedArrayImports_runJsTypedArrayTests_static") +fileprivate func bjs_JSTypedArrayImports_runJsTypedArrayTests_static_extern() -> Void +#else +fileprivate func bjs_JSTypedArrayImports_runJsTypedArrayTests_static_extern() -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSTypedArrayImports_runJsTypedArrayTests_static() -> Void { + return bjs_JSTypedArrayImports_runJsTypedArrayTests_static_extern() +} + +func _$JSTypedArrayImports_jsCreateUint8Array() throws(JSException) -> JSUint8Array { + let ret = bjs_JSTypedArrayImports_jsCreateUint8Array_static() + if let error = _swift_js_take_exception() { + throw error + } + return JSUint8Array.bridgeJSLiftReturn(ret) +} + +func _$JSTypedArrayImports_jsRoundTripUint8Array(_ v: JSUint8Array) throws(JSException) -> JSUint8Array { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_JSTypedArrayImports_jsRoundTripUint8Array_static(vValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUint8Array.bridgeJSLiftReturn(ret) +} + +func _$JSTypedArrayImports_jsRoundTripFloat32Array(_ v: JSFloat32Array) throws(JSException) -> JSFloat32Array { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_JSTypedArrayImports_jsRoundTripFloat32Array_static(vValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSFloat32Array.bridgeJSLiftReturn(ret) +} + +func _$JSTypedArrayImports_jsRoundTripFloat64Array(_ v: JSFloat64Array) throws(JSException) -> JSFloat64Array { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_JSTypedArrayImports_jsRoundTripFloat64Array_static(vValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSFloat64Array.bridgeJSLiftReturn(ret) +} + +func _$JSTypedArrayImports_jsRoundTripInt32Array(_ v: JSInt32Array) throws(JSException) -> JSInt32Array { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_JSTypedArrayImports_jsRoundTripInt32Array_static(vValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSInt32Array.bridgeJSLiftReturn(ret) +} + +func _$JSTypedArrayImports_runJsTypedArrayTests() throws(JSException) -> Void { + bjs_JSTypedArrayImports_runJsTypedArrayTests_static() + if let error = _swift_js_take_exception() { + throw error + } +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_MyJSClassInternal_init") fileprivate func bjs_MyJSClassInternal_init_extern() -> Int32 diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 484620191..a28843142 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -49,7 +49,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -97,7 +98,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -145,7 +147,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -202,7 +205,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -250,7 +254,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -298,7 +303,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -591,7 +597,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -641,7 +648,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -684,7 +692,8 @@ "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -724,7 +733,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -797,8 +807,11 @@ "isStatic" : false, "name" : "nameCount", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } }, @@ -1095,6 +1108,36 @@ } } + }, + { + "abiName" : "bjs___Swift_Foundation_UUID_static_fromValue", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "fromValue", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "UUID" + } + }, + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_UUID" + } + } } ], "name" : "UUID", @@ -1103,7 +1146,21 @@ "Foundation" ], "properties" : [ + { + "isReadonly" : true, + "isStatic" : true, + "name" : "placeholder", + "staticContext" : { + "className" : { + "_0" : "__Swift_Foundation_UUID" + } + }, + "type" : { + "string" : { + } + } + } ], "swiftCallName" : "UUID" }, @@ -2928,7 +2985,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3014,7 +3072,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3056,7 +3115,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3097,7 +3157,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3148,7 +3209,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3196,7 +3258,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3243,7 +3306,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3279,7 +3343,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3315,7 +3380,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3359,7 +3425,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3406,7 +3473,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3452,7 +3520,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3494,7 +3563,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3530,7 +3600,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3569,7 +3640,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3604,7 +3676,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3645,7 +3718,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3694,7 +3768,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3742,7 +3817,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3789,7 +3865,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3825,7 +3902,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3863,7 +3941,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3899,7 +3978,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3924,7 +4004,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3965,7 +4046,101 @@ "string" : { } - } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_TextProcessor_processVector", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processVector", + "parameters" : [ + { + "label" : "_", + "name" : "callback", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsSd_8Vector2DV", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + { + "double" : { + + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "Vector2D" + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : false + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_TextProcessor_processOptionalVector", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "processOptionalVector", + "parameters" : [ + { + "label" : "_", + "name" : "callback", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsSd_Sq8Vector2DV", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + { + "double" : { + + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Vector2D" + } + }, + "_1" : "null" + } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -7153,6 +7328,7 @@ } ], "emitStyle" : "const", + "explicitAccessControl" : "public", "name" : "Direction", "staticMethods" : [ @@ -8523,15 +8699,21 @@ "label" : "_", "name" : "value", "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } ], "returnType" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } }, "staticContext" : { @@ -8983,78 +9165,204 @@ ], "emitStyle" : "const", - "name" : "IntegerTypesSupportExports", + "name" : "NestedStructGroupA", "staticMethods" : [ { - "abiName" : "bjs_IntegerTypesSupportExports_static_roundTripInt", + "abiName" : "bjs_NestedStructGroupA_static_roundtripMetadata", "effects" : { "isAsync" : false, "isStatic" : true, "isThrows" : false }, - "name" : "roundTripInt", + "name" : "roundtripMetadata", "namespace" : [ - "IntegerTypesSupportExports" + "NestedStructGroupA" ], "parameters" : [ { "label" : "_", - "name" : "v", + "name" : "m", "type" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } + "swiftStruct" : { + "_0" : "NestedStructGroupA.Metadata" } } } ], "returnType" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } + "swiftStruct" : { + "_0" : "NestedStructGroupA.Metadata" } }, "staticContext" : { "namespaceEnum" : { - "_0" : "IntegerTypesSupportExports" + "_0" : "NestedStructGroupA" } } - }, + } + ], + "staticProperties" : [ + + ], + "swiftCallName" : "NestedStructGroupA", + "tsFullPath" : "NestedStructGroupA" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "NestedStructGroupB", + "staticMethods" : [ { - "abiName" : "bjs_IntegerTypesSupportExports_static_roundTripUInt", + "abiName" : "bjs_NestedStructGroupB_static_roundtripMetadata", "effects" : { "isAsync" : false, "isStatic" : true, "isThrows" : false }, - "name" : "roundTripUInt", + "name" : "roundtripMetadata", "namespace" : [ - "IntegerTypesSupportExports" + "NestedStructGroupB" ], "parameters" : [ { "label" : "_", - "name" : "v", + "name" : "m", "type" : { - "integer" : { - "_0" : { - "isSigned" : false, - "width" : "word" - } + "swiftStruct" : { + "_0" : "NestedStructGroupB.Metadata" } } } ], "returnType" : { - "integer" : { - "_0" : { - "isSigned" : false, - "width" : "word" - } + "swiftStruct" : { + "_0" : "NestedStructGroupB.Metadata" + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "NestedStructGroupB" + } + } + } + ], + "staticProperties" : [ + + ], + "swiftCallName" : "NestedStructGroupB", + "tsFullPath" : "NestedStructGroupB" + }, + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "red" + }, + { + "associatedValues" : [ + + ], + "name" : "yellow" + }, + { + "associatedValues" : [ + + ], + "name" : "green" + } + ], + "emitStyle" : "const", + "name" : "LightColor", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "LightColor", + "tsFullPath" : "LightColor" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "IntegerTypesSupportExports", + "staticMethods" : [ + { + "abiName" : "bjs_IntegerTypesSupportExports_static_roundTripInt", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripInt", + "namespace" : [ + "IntegerTypesSupportExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "IntegerTypesSupportExports" + } + } + }, + { + "abiName" : "bjs_IntegerTypesSupportExports_static_roundTripUInt", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripUInt", + "namespace" : [ + "IntegerTypesSupportExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "integer" : { + "_0" : { + "isSigned" : false, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : false, + "width" : "word" + } } }, "staticContext" : { @@ -9385,6 +9693,152 @@ { "cases" : [ + ], + "emitStyle" : "const", + "name" : "JSTypedArrayExports", + "staticMethods" : [ + { + "abiName" : "bjs_JSTypedArrayExports_static_roundTripUint8Array", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripUint8Array", + "namespace" : [ + "JSTypedArrayExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "JSTypedArrayExports" + } + } + }, + { + "abiName" : "bjs_JSTypedArrayExports_static_roundTripFloat32Array", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripFloat32Array", + "namespace" : [ + "JSTypedArrayExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "JSTypedArrayExports" + } + } + }, + { + "abiName" : "bjs_JSTypedArrayExports_static_roundTripFloat64Array", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripFloat64Array", + "namespace" : [ + "JSTypedArrayExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "JSTypedArrayExports" + } + } + }, + { + "abiName" : "bjs_JSTypedArrayExports_static_roundTripInt32Array", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundTripInt32Array", + "namespace" : [ + "JSTypedArrayExports" + ], + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "JSTypedArrayExports" + } + } + } + ], + "staticProperties" : [ + + ], + "swiftCallName" : "JSTypedArrayExports", + "tsFullPath" : "JSTypedArrayExports" + }, + { + "cases" : [ + ], "emitStyle" : "const", "name" : "OptionalSupportExports", @@ -10595,7 +11049,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13597,7 +14052,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13647,7 +14103,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13699,7 +14156,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -14708,19 +15166,98 @@ "methods" : [ ], - "name" : "Point", + "name" : "Metadata", + "namespace" : [ + "NestedStructGroupA" + ], "properties" : [ { "isReadonly" : true, "isStatic" : false, - "name" : "x", + "name" : "label", + "namespace" : [ + "NestedStructGroupA" + ], "type" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } - } + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "count", + "namespace" : [ + "NestedStructGroupA" + ], + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "swiftCallName" : "NestedStructGroupA.Metadata" + }, + { + "methods" : [ + + ], + "name" : "Metadata", + "namespace" : [ + "NestedStructGroupB" + ], + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "tag", + "namespace" : [ + "NestedStructGroupB" + ], + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "value", + "namespace" : [ + "NestedStructGroupB" + ], + "type" : { + "double" : { + + } + } + } + ], + "swiftCallName" : "NestedStructGroupB.Metadata" + }, + { + "methods" : [ + + ], + "name" : "Point", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "x", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } } }, { @@ -15045,8 +15582,11 @@ } }, "type" : { - "int" : { - + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } } } } @@ -16314,7 +16854,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "id", @@ -16328,6 +16870,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "id", "type" : { "string" : { @@ -16348,6 +16891,7 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], @@ -16360,6 +16904,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsIntArrayLength", "parameters" : [ { @@ -16388,6 +16938,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripIntArray", "parameters" : [ { @@ -16420,6 +16976,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripNumberArray", "parameters" : [ { @@ -16446,6 +17008,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripStringArray", "parameters" : [ { @@ -16472,6 +17040,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripBoolArray", "parameters" : [ { @@ -16498,6 +17072,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSValueArray", "parameters" : [ { @@ -16524,6 +17104,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSObjectArray", "parameters" : [ { @@ -16550,6 +17136,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSClassArray", "parameters" : [ { @@ -16576,6 +17168,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalIntArray", "parameters" : [ { @@ -16618,6 +17216,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringArray", "parameters" : [ { @@ -16654,6 +17258,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalBoolArray", "parameters" : [ { @@ -16690,6 +17300,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArray", "parameters" : [ { @@ -16726,6 +17342,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSObjectArray", "parameters" : [ { @@ -16762,6 +17384,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSClassArray", "parameters" : [ { @@ -16798,6 +17426,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsSumNumberArray", "parameters" : [ { @@ -16820,6 +17454,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCreateNumberArray", "parameters" : [ @@ -16835,6 +17475,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsArraySupportTests", "parameters" : [ @@ -16855,42 +17501,28 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ ], - "name" : "ClosureSupportImports", + "name" : "AsyncImportImports", "setters" : [ ], "staticMethods" : [ { - "name" : "jsApplyVoid", + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripVoid", "parameters" : [ - { - "name" : "callback", - "type" : { - "closure" : { - "_0" : { - "isAsync" : false, - "isThrows" : false, - "mangleName" : "20BridgeJSRuntimeTestsy_y", - "moduleName" : "BridgeJSRuntimeTests", - "parameters" : [ - - ], - "returnType" : { - "void" : { - } - } - }, - "useJSTypedClosure" : true - } - } - } ], "returnType" : { "void" : { @@ -16899,139 +17531,489 @@ } }, { - "name" : "jsApplyBool", + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripNumber", "parameters" : [ { - "name" : "callback", + "name" : "v", "type" : { - "closure" : { - "_0" : { - "isAsync" : false, - "isThrows" : false, - "mangleName" : "20BridgeJSRuntimeTestsy_Sb", - "moduleName" : "BridgeJSRuntimeTests", - "parameters" : [ - - ], - "returnType" : { - "bool" : { + "double" : { - } - } - }, - "useJSTypedClosure" : true } } } ], "returnType" : { - "bool" : { + "double" : { } } }, { - "name" : "jsApplyInt", + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripBool", "parameters" : [ { - "name" : "value", - "type" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } - } - } - }, - { - "name" : "transform", + "name" : "v", "type" : { - "closure" : { - "_0" : { - "isAsync" : false, - "isThrows" : false, - "mangleName" : "20BridgeJSRuntimeTestsSi_Si", - "moduleName" : "BridgeJSRuntimeTests", - "parameters" : [ - { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } - } - } - ], - "returnType" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } - } - } - }, - "useJSTypedClosure" : true + "bool" : { + } } } ], "returnType" : { - "integer" : { - "_0" : { - "isSigned" : true, - "width" : "word" - } + "bool" : { + } } }, { - "name" : "jsApplyDouble", + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripString", "parameters" : [ { - "name" : "value", + "name" : "v", "type" : { - "double" : { + "string" : { } } - }, + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalString", + "parameters" : [ { - "name" : "transform", + "name" : "v", "type" : { - "closure" : { + "nullable" : { "_0" : { - "isAsync" : false, - "isThrows" : false, - "mangleName" : "20BridgeJSRuntimeTestsSd_Sd", - "moduleName" : "BridgeJSRuntimeTests", - "parameters" : [ - { - "double" : { - - } - } - ], - "returnType" : { - "double" : { + "string" : { - } } }, - "useJSTypedClosure" : true + "_1" : "null" } } } ], "returnType" : { - "double" : { + "nullable" : { + "_0" : { + "string" : { + } + }, + "_1" : "null" } } }, { - "name" : "jsApplyString", + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalNumber", + "parameters" : [ + { + "name" : "v", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripBoolArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripIntArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripStringArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripFeatureFlag", + "parameters" : [ + { + "name" : "v", + "type" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + } + ], + "returnType" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + } + ] + } + ] + }, + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "internal", + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "ClosureSupportImports", + "setters" : [ + + ], + "staticMethods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsApplyVoid", + "parameters" : [ + { + "name" : "callback", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsy_y", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsApplyBool", + "parameters" : [ + { + "name" : "callback", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsy_Sb", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + + ], + "returnType" : { + "bool" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsApplyInt", + "parameters" : [ + { + "name" : "value", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "name" : "transform", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsSi_Si", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsApplyDouble", + "parameters" : [ + { + "name" : "value", + "type" : { + "double" : { + + } + } + }, + { + "name" : "transform", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsSd_Sd", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + { + "double" : { + + } + } + ], + "returnType" : { + "double" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsApplyString", "parameters" : [ { "name" : "value", @@ -17061,7 +18043,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17075,6 +18058,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyJSObject", "parameters" : [ { @@ -17105,7 +18094,8 @@ "jsObject" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17119,6 +18109,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeIntToInt", "parameters" : [ { @@ -17157,13 +18153,20 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeDoubleToDouble", "parameters" : [ { @@ -17193,13 +18196,20 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeStringToString", "parameters" : [ { @@ -17229,13 +18239,20 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallTwice", "parameters" : [ { @@ -17272,7 +18289,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17289,6 +18307,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallBinary", "parameters" : [ { @@ -17325,7 +18349,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17342,6 +18367,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallTriple", "parameters" : [ { @@ -17386,7 +18417,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17403,6 +18435,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallAfterRelease", "parameters" : [ { @@ -17421,7 +18459,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17435,6 +18474,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsOptionalInvoke", "parameters" : [ { @@ -17455,7 +18500,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17472,6 +18518,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsStoreClosure", "parameters" : [ { @@ -17490,7 +18542,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17504,6 +18557,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallStoredClosure", "parameters" : [ @@ -17515,6 +18574,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsHeapCount", "parameters" : [ @@ -17529,6 +18594,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsClosureSupportTests", "parameters" : [ @@ -17549,6 +18620,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -17561,6 +18633,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsDefaultArgumentTests", "parameters" : [ @@ -17581,6 +18659,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -17593,6 +18672,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryInt", "parameters" : [ { @@ -17625,6 +18710,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryBool", "parameters" : [ { @@ -17651,6 +18742,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryDouble", "parameters" : [ { @@ -17677,6 +18774,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryJSObject", "parameters" : [ { @@ -17703,6 +18806,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryJSValue", "parameters" : [ { @@ -17729,6 +18838,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryDoubleArray", "parameters" : [ { @@ -17772,7 +18887,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -17786,6 +18903,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "value", "type" : { "string" : { @@ -17810,6 +18928,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripVoid", "parameters" : [ @@ -17821,6 +18945,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripNumber", "parameters" : [ { @@ -17839,6 +18969,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripBool", "parameters" : [ { @@ -17857,6 +18993,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripString", "parameters" : [ { @@ -17875,6 +19017,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSValue", "parameters" : [ { @@ -17893,6 +19041,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrVoid", "parameters" : [ { @@ -17911,6 +19065,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrNumber", "parameters" : [ { @@ -17929,6 +19089,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrBool", "parameters" : [ { @@ -17947,6 +19113,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrString", "parameters" : [ { @@ -17965,6 +19137,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripFeatureFlag", "parameters" : [ { @@ -17985,17 +19163,53 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, "name" : "runAsyncWorks", "parameters" : [ + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "fetchWeatherData", + "parameters" : [ + { + "name" : "city", + "type" : { + "string" : { + + } + } + } ], "returnType" : { "jsObject" : { - "_0" : "JSPromise" + "_0" : "WeatherData" } } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "$jsWeirdFunction", "name" : "_jsWeirdFunction", "parameters" : [ @@ -18008,6 +19222,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "name" : "parseInt", "parameters" : [ @@ -18029,6 +19249,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "from" : "global", "name" : "globalObject1", "type" : { @@ -18040,7 +19261,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -18062,6 +19285,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -18070,6 +19294,7 @@ } }, { + "accessLevel" : "internal", "name" : "prefix", "type" : { "string" : { @@ -18080,6 +19305,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "greet", "parameters" : [ @@ -18091,6 +19322,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "changeName", "parameters" : [ { @@ -18112,6 +19349,7 @@ "name" : "JsGreeter", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -18126,7 +19364,80 @@ ] }, { + "accessLevel" : "internal", + "getters" : [ + { + "accessLevel" : "internal", + "name" : "temperature", + "type" : { + "double" : { + + } + } + }, + { + "accessLevel" : "internal", + "name" : "description", + "type" : { + "string" : { + + } + } + }, + { + "accessLevel" : "internal", + "name" : "humidity", + "type" : { + "double" : { + + } + } + } + ], + "methods" : [ + + ], + "name" : "WeatherData", + "setters" : [ + { + "accessLevel" : "internal", + "functionName" : "temperature_set", + "name" : "temperature", + "type" : { + "double" : { + + } + } + }, + { + "accessLevel" : "internal", + "functionName" : "description_set", + "name" : "description", + "type" : { + "string" : { + + } + } + }, + { + "accessLevel" : "internal", + "functionName" : "humidity_set", + "name" : "humidity", + "type" : { + "double" : { + + } + } + } + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -18137,6 +19448,12 @@ "jsName" : "$WeirdClass", "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "method-with-dashes", "name" : "method_with_dashes", "parameters" : [ @@ -18158,7 +19475,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -18175,6 +19494,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -18192,6 +19517,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { @@ -18210,6 +19541,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -18221,6 +19558,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeDefault", "parameters" : [ @@ -18232,6 +19575,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "with-dashes", "name" : "with_dashes", "parameters" : [ @@ -18246,7 +19595,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -18277,6 +19628,7 @@ "from" : "global", "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -18285,6 +19637,7 @@ } }, { + "accessLevel" : "internal", "name" : "age", "type" : { "double" : { @@ -18293,6 +19646,7 @@ } }, { + "accessLevel" : "internal", "name" : "isCat", "type" : { "bool" : { @@ -18303,6 +19657,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "bark", "parameters" : [ @@ -18314,6 +19674,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "getIsCat", "parameters" : [ @@ -18328,6 +19694,7 @@ "name" : "Animal", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -18337,6 +19704,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "age_set", "name" : "age", "type" : { @@ -18346,6 +19714,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "isCat_set", "name" : "isCat", "type" : { @@ -18364,6 +19733,43 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripLightColor", + "parameters" : [ + { + "name" : "value", + "type" : { + "caseEnum" : { + "_0" : "LightColor" + } + } + } + ], + "returnType" : { + "caseEnum" : { + "_0" : "LightColor" + } + } + } + ], + "types" : [ + + ] + }, + { + "functions" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsTranslatePoint", "parameters" : [ { @@ -18402,6 +19808,40 @@ "_0" : "Point" } } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripOptionalPoint", + "parameters" : [ + { + "name" : "point", + "type" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Point" + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "swiftStruct" : { + "_0" : "Point" + } + }, + "_1" : "null" + } + } } ], "types" : [ @@ -18414,6 +19854,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -18426,6 +19867,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt", "parameters" : [ { @@ -18450,6 +19897,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt", "parameters" : [ { @@ -18474,6 +19927,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt8", "parameters" : [ { @@ -18498,6 +19957,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt8", "parameters" : [ { @@ -18522,6 +19987,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt16", "parameters" : [ { @@ -18546,6 +20017,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt16", "parameters" : [ { @@ -18570,6 +20047,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt32", "parameters" : [ { @@ -18594,6 +20077,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt32", "parameters" : [ { @@ -18618,6 +20107,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt64", "parameters" : [ { @@ -18642,6 +20137,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt64", "parameters" : [ { @@ -18666,6 +20167,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsIntegerTypesSupportTests", "parameters" : [ @@ -18686,7 +20193,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "numbers", @@ -18719,6 +20228,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "numbers", "type" : { "array" : { @@ -18734,6 +20244,7 @@ } }, { + "accessLevel" : "internal", "name" : "labels", "type" : { "array" : { @@ -18748,6 +20259,12 @@ ], "methods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "concatNumbers", "parameters" : [ { @@ -18780,6 +20297,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "concatLabels", "parameters" : [ { @@ -18806,6 +20329,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "firstLabel", "parameters" : [ { @@ -18831,6 +20360,7 @@ "name" : "JSClassWithArrayMembers", "setters" : [ { + "accessLevel" : "internal", "functionName" : "numbers_set", "name" : "numbers", "type" : { @@ -18847,6 +20377,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "labels_set", "name" : "labels", "type" : { @@ -18865,6 +20396,7 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], @@ -18877,6 +20409,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeJSClassWithArrayMembers", "parameters" : [ { @@ -18923,7 +20461,161 @@ ], "types" : [ { + "accessLevel" : "internal", + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "JSTypedArrayImports", + "setters" : [ + + ], + "staticMethods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsCreateUint8Array", + "parameters" : [ + + ], + "returnType" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripUint8Array", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSUint8Array" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripFloat32Array", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat32Array" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripFloat64Array", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSFloat64Array" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsRoundTripInt32Array", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "JSInt32Array" + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "runJsTypedArrayTests", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ] + } + ] + }, + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -18944,7 +20636,9 @@ ] }, { + "accessLevel" : "public", "constructor" : { + "accessLevel" : "public", "parameters" : [ ] @@ -18965,7 +20659,9 @@ ] }, { + "accessLevel" : "package", "constructor" : { + "accessLevel" : "package", "parameters" : [ ] @@ -18990,6 +20686,12 @@ { "functions" : [ { + "accessLevel" : "package", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPackageAccess", "parameters" : [ @@ -19001,6 +20703,12 @@ } }, { + "accessLevel" : "public", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPublicAccess", "parameters" : [ @@ -19012,6 +20720,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithInternalAccess", "parameters" : [ @@ -19023,6 +20737,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithFilePrivateAccess", "parameters" : [ @@ -19034,6 +20754,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPrivateAccess", "parameters" : [ @@ -19055,6 +20781,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -19067,6 +20794,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalNumberNull", "parameters" : [ { @@ -19101,6 +20834,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalNumberUndefined", "parameters" : [ { @@ -19135,6 +20874,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringNull", "parameters" : [ { @@ -19163,6 +20908,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringUndefined", "parameters" : [ { @@ -19191,6 +20942,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArrayNull", "parameters" : [ { @@ -19227,6 +20984,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArrayUndefined", "parameters" : [ { @@ -19263,6 +21026,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringToStringDictionaryNull", "parameters" : [ { @@ -19299,6 +21068,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringToStringDictionaryUndefined", "parameters" : [ { @@ -19335,6 +21110,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsOptionalSupportTests", "parameters" : [ @@ -19352,6 +21133,12 @@ { "functions" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "name" : "gc", "parameters" : [ @@ -19366,6 +21153,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -19378,6 +21166,12 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripGreeter", "parameters" : [ { @@ -19396,6 +21190,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUUID", "parameters" : [ { @@ -19414,6 +21214,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalGreeter", "parameters" : [ { @@ -19442,6 +21248,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsConsumeLeakCheck", "parameters" : [ { @@ -19460,6 +21272,12 @@ } }, { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsConsumeOptionalLeakCheck", "parameters" : [ { @@ -19488,5 +21306,8 @@ } ] }, - "moduleName" : "BridgeJSRuntimeTests" + "moduleName" : "BridgeJSRuntimeTests", + "usedExternalModules" : [ + + ] } \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift index 8f02af2ef..2bb9158b9 100644 --- a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift @@ -1,6 +1,14 @@ import XCTest import JavaScriptKit +@JS enum LightColor { + case red + case yellow + case green +} + +@JSFunction func jsRoundTripLightColor(_ value: LightColor) throws(JSException) -> LightColor + class ImportAPITests: XCTestCase { func testRoundTripVoid() throws { try jsRoundTripVoid() @@ -59,6 +67,19 @@ class ImportAPITests: XCTestCase { } } + func testRoundTripOptionalStruct() throws { + let p = try jsRoundTripOptionalPoint(Point(x: 3, y: 4)) + XCTAssertEqual(p?.x, 3) + XCTAssertEqual(p?.y, 4) + XCTAssertNil(try jsRoundTripOptionalPoint(nil)) + } + + func testRoundTripCaseEnum() throws { + for v in [LightColor.red, .yellow, .green] { + try XCTAssertEqual(jsRoundTripLightColor(v), v) + } + } + func ensureThrows(_ f: (Bool) throws(JSException) -> T) throws { do { _ = try f(true) diff --git a/Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift b/Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift index 41929772e..f981a5e01 100644 --- a/Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift +++ b/Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift @@ -7,3 +7,5 @@ struct Point { } @JSFunction func jsTranslatePoint(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point + +@JSFunction func jsRoundTripOptionalPoint(_ point: Point?) throws(JSException) -> Point? diff --git a/Tests/BridgeJSRuntimeTests/JSTypedArrayTests.swift b/Tests/BridgeJSRuntimeTests/JSTypedArrayTests.swift new file mode 100644 index 000000000..25045ecc8 --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/JSTypedArrayTests.swift @@ -0,0 +1,53 @@ +import XCTest +import JavaScriptKit + +@JS enum JSTypedArrayExports { + @JS static func roundTripUint8Array(_ v: JSUint8Array) -> JSUint8Array { v } + @JS static func roundTripFloat32Array(_ v: JSFloat32Array) -> JSFloat32Array { v } + @JS static func roundTripFloat64Array(_ v: JSFloat64Array) -> JSFloat64Array { v } + @JS static func roundTripInt32Array(_ v: JSInt32Array) -> JSInt32Array { v } +} + +@JSClass struct JSTypedArrayImports { + @JSFunction static func jsCreateUint8Array() throws(JSException) -> JSUint8Array + @JSFunction static func jsRoundTripUint8Array(_ v: JSUint8Array) throws(JSException) -> JSUint8Array + @JSFunction static func jsRoundTripFloat32Array(_ v: JSFloat32Array) throws(JSException) -> JSFloat32Array + @JSFunction static func jsRoundTripFloat64Array(_ v: JSFloat64Array) throws(JSException) -> JSFloat64Array + @JSFunction static func jsRoundTripInt32Array(_ v: JSInt32Array) throws(JSException) -> JSInt32Array + @JSFunction static func runJsTypedArrayTests() throws(JSException) +} + +final class JSTypedArrayTests: XCTestCase { + func testRunJsTypedArrayTests() throws { + try JSTypedArrayImports.runJsTypedArrayTests() + } + + func testRoundTripUint8Array() throws { + let arr = JSUint8Array([1, 2, 3, 255]) + let result = try JSTypedArrayImports.jsRoundTripUint8Array(arr) + XCTAssertEqual(result.length, 4) + } + + func testCreateUint8Array() throws { + let result = try JSTypedArrayImports.jsCreateUint8Array() + XCTAssertEqual(result.length, 3) + } + + func testRoundTripFloat32Array() throws { + let arr = JSFloat32Array([1.0, 2.5, 3.0]) + let result = try JSTypedArrayImports.jsRoundTripFloat32Array(arr) + XCTAssertEqual(result.length, 3) + } + + func testRoundTripFloat64Array() throws { + let arr = JSFloat64Array([1.0, 2.5, 3.14159]) + let result = try JSTypedArrayImports.jsRoundTripFloat64Array(arr) + XCTAssertEqual(result.length, 3) + } + + func testRoundTripInt32Array() throws { + let arr = JSInt32Array([1, -2, 2_147_483_647]) + let result = try JSTypedArrayImports.jsRoundTripInt32Array(arr) + XCTAssertEqual(result.length, 3) + } +} diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs new file mode 100644 index 000000000..f64531d4a --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs @@ -0,0 +1,46 @@ +// @ts-check + +import assert from 'node:assert'; + +/** + * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports["AsyncImportImports"]} + */ +export function getImports(importsContext) { + return { + jsAsyncRoundTripVoid: () => { + return Promise.resolve(); + }, + jsAsyncRoundTripNumber: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripBool: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripString: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripOptionalString: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripOptionalNumber: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripBoolArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripIntArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripStringArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripFeatureFlag: (v) => { + return Promise.resolve(v); + }, + }; +} + +/** @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ +export async function runAsyncWorksTests(exports) { + await exports.asyncRoundTripVoid(); +} diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/ClosureSupportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/ClosureSupportTests.mjs index ebe00548b..bab496d09 100644 --- a/Tests/BridgeJSRuntimeTests/JavaScript/ClosureSupportTests.mjs +++ b/Tests/BridgeJSRuntimeTests/JavaScript/ClosureSupportTests.mjs @@ -355,6 +355,17 @@ export function runJsClosureSupportTests(exports) { }); assert.equal(optDpResult, "DP: 7 | DP: null"); + const vectorMagnitude = processor.processVector((value) => ({ + dx: value, + dy: 4.0, + })); + assert.equal(vectorMagnitude, 5.0); + + const optVectorResult = processor.processOptionalVector((value) => + value > 0 ? { dx: value, dy: value * 2 } : null, + ); + assert.equal(optVectorResult, "(2.0,4.0) | nil"); + processor.release(); const intToInt = exports.ClosureSupportExports.makeIntToInt(10); diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/JSTypedArrayTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/JSTypedArrayTests.mjs new file mode 100644 index 000000000..f29c248f9 --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/JavaScript/JSTypedArrayTests.mjs @@ -0,0 +1,77 @@ +// @ts-check + +import assert from 'node:assert'; + +/** + * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports["JSTypedArrayImports"]} + */ +export function getImports(importsContext) { + return { + jsCreateUint8Array: function () { + return new Uint8Array([10, 20, 30]); + }, + jsRoundTripUint8Array: function (arr) { + assert.ok(arr instanceof Uint8Array, 'Expected Uint8Array'); + return arr; + }, + jsRoundTripFloat32Array: function (arr) { + assert.ok(arr instanceof Float32Array, 'Expected Float32Array'); + return arr; + }, + jsRoundTripFloat64Array: function (arr) { + assert.ok(arr instanceof Float64Array, 'Expected Float64Array'); + return arr; + }, + jsRoundTripInt32Array: function (arr) { + assert.ok(arr instanceof Int32Array, 'Expected Int32Array'); + return arr; + }, + runJsTypedArrayTests: () => { + const exports = importsContext.getExports(); + if (!exports) { throw new Error("No exports!?"); } + runJsTypedArrayTests(exports); + }, + }; +} + +/** + * JSTypedArray bridging coverage for BridgeJS runtime tests. + * @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} rootExports + */ +export function runJsTypedArrayTests(rootExports) { + const exports = rootExports.JSTypedArrayExports; + + // Uint8Array round-trip + const u8 = new Uint8Array([1, 2, 3, 255]); + const u8Result = exports.roundTripUint8Array(u8); + assert.ok(u8Result instanceof Uint8Array, 'Expected Uint8Array back from Swift'); + assert.equal(u8Result.length, 4); + assert.deepEqual(Array.from(u8Result), [1, 2, 3, 255]); + + // Float32Array round-trip + const f32 = new Float32Array([1.0, 2.5, 3.0]); + const f32Result = exports.roundTripFloat32Array(f32); + assert.ok(f32Result instanceof Float32Array, 'Expected Float32Array back from Swift'); + assert.equal(f32Result.length, 3); + assert.deepEqual(Array.from(f32Result), [1.0, 2.5, 3.0]); + + // Float64Array round-trip + const f64 = new Float64Array([1.0, 2.5, 3.14159]); + const f64Result = exports.roundTripFloat64Array(f64); + assert.ok(f64Result instanceof Float64Array, 'Expected Float64Array back from Swift'); + assert.equal(f64Result.length, 3); + assert.deepEqual(Array.from(f64Result), [1.0, 2.5, 3.14159]); + + // Int32Array round-trip + const i32 = new Int32Array([1, -2, 2147483647]); + const i32Result = exports.roundTripInt32Array(i32); + assert.ok(i32Result instanceof Int32Array, 'Expected Int32Array back from Swift'); + assert.equal(i32Result.length, 3); + assert.deepEqual(Array.from(i32Result), [1, -2, 2147483647]); + + // Empty typed array + const emptyU8 = new Uint8Array([]); + const emptyResult = exports.roundTripUint8Array(emptyU8); + assert.ok(emptyResult instanceof Uint8Array, 'Expected Uint8Array for empty array'); + assert.equal(emptyResult.length, 0); +} diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs index 6576876da..ae445d3f4 100644 --- a/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs +++ b/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs @@ -80,6 +80,10 @@ export function runJsOptionalSupportTests(rootExports) { assert.equal(exports.roundTripOptionalIntRawValueEnum(HttpStatus.Ok), HttpStatusValues.Ok); assert.equal(exports.roundTripOptionalInt64RawValueEnum(FileSize.Tiny), FileSizeValues.Tiny); assert.equal(exports.roundTripOptionalUInt64RawValueEnum(SessionId.Active), SessionIdValues.Active); + // The `none` case lowers the i64/u64 placeholder as a BigInt (`0n`); a plain `0` + // would throw "Cannot convert 0 to a BigInt" when calling the Wasm export. + assert.equal(exports.roundTripOptionalInt64RawValueEnum(null), null); + assert.equal(exports.roundTripOptionalUInt64RawValueEnum(null), null); assert.equal(exports.roundTripOptionalTSEnum(TSDirection.North), TSDirection.North); assert.equal(exports.roundTripOptionalTSStringEnum(TSTheme.Light), TSTheme.Light); assert.equal(exports.roundTripOptionalNamespacedEnum(Networking.API.Method.Get), Networking.API.Method.Get); diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index e8533a3d8..9fef391c1 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -26,6 +26,13 @@ export class JsGreeter { export function runAsyncWorks(): Promise; +export interface WeatherData { + temperature: number; + description: string; + humidity: number; +} +export function fetchWeatherData(city: string): Promise; + // jsName tests export function $jsWeirdFunction(): number; diff --git a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift index f743d8ef0..69b3390dc 100644 --- a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift +++ b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift @@ -618,6 +618,152 @@ final class WebWorkerTaskExecutorTests: XCTestCase { XCTAssertEqual(object["test"].string!, "Hello, World!") } + func testRemoteMainToWorkerAccess() async throws { + let object = JSObject.global.Object.function!.new() + object["value"] = 42 + let remote = JSRemote(object) + + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + let task = Task(executorPreference: executor) { + try await remote.withJSObject { object in + object["value"].number! + } + } + + let value = try await task.value + XCTAssertEqual(value, 42) + } + + func testRemoteWorkerToMainAccess() async throws { + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + let task = Task(executorPreference: executor) { + let object = JSObject.global.Object.function!.new() + object["value"] = 99 + let remote = JSRemote(object) + return remote + } + + let remote = await task.value + let result = try await remote.withJSObject { object in + object["value"].number! + } + XCTAssertEqual(result, 99) + } + + func testRemoteSameThreadFastPath() async throws { + let object = JSObject.global.Object.function!.new() + object["flag"] = 1 + let remote = JSRemote(object) + + let result = try await remote.withJSObject { object in + object["flag"].number! + } + XCTAssertEqual(result, 1) + } + + func testRemoteCanBeUsedMultipleTimesAcrossThreads() async throws { + let object = JSObject.global.Object.function!.new() + object["count"] = 0 + let remote = JSRemote(object) + + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + let task = Task(executorPreference: executor) { + let first = try await remote.withJSObject { object in + let nextValue = object["count"].number! + 1 + object["count"] = .number(nextValue) + return nextValue + } + let second = try await remote.withJSObject { object in + let nextValue = object["count"].number! + 1 + object["count"] = .number(nextValue) + return nextValue + } + return (first, second) + } + + let (first, second) = try await task.value + XCTAssertEqual(first, 1) + XCTAssertEqual(second, 2) + XCTAssertEqual(object["count"].number!, 2) + } + + func testRemoteConcurrentAccessFromWorkerTasks() async throws { + let object = JSObject.global.Object.function!.new() + object["value"] = 42 + let remote = JSRemote(object) + + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 2) + defer { executor.terminate() } + + let results = try await withThrowingTaskGroup(of: Int.self, returning: [Int].self) { group in + for _ in 0..<8 { + group.addTask(executorPreference: executor) { + try await remote.withJSObject { object in + Int(object["value"].number!) + } + } + } + + var results: [Int] = [] + for try await value in group { + results.append(value) + } + return results + } + + XCTAssertEqual(results.count, 8) + XCTAssertEqual(results, Array(repeating: 42, count: 8)) + } + + func testRemoteMutationIsVisibleOnOwnerThread() async throws { + let object = JSObject.global.Object.function!.new() + object["value"] = 10 + let remote = JSRemote(object) + + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + let task = Task(executorPreference: executor) { + try await remote.withJSObject { object in + object["value"] = .number(object["value"].number! + 5) + } + } + + _ = try await task.value + XCTAssertEqual(object["value"].number!, 15) + } + + func testRemoteThrowsTypedError() async throws { + struct TestError: Error, Equatable { + let message: String + } + + let object = JSObject.global.Object.function!.new() + let remote = JSRemote(object) + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + let task = Task(executorPreference: executor) { + do { + _ = try await remote.withJSObject { _ in + throw TestError(message: "boom") + } + return "unexpected" + } catch { + return String(describing: error) + } + } + + let errorDescription = try await task.value + XCTAssertTrue(errorDescription.contains("boom"), errorDescription) + } + func testThrowJSExceptionAcrossThreads() async throws { let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) let task = Task(executorPreference: executor) { @@ -643,5 +789,30 @@ final class WebWorkerTaskExecutorTests: XCTestCase { // await task.value // executor.terminate() // } + + func testDeinitJSStringOnDifferentThread() async throws { + final class Box: @unchecked Sendable { + var string: JSString? + init(_ string: JSString) { self.string = string } + } + + let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1) + defer { executor.terminate() } + + // Force JS ref allocation on the main thread so ownerTid = main thread. + var string: JSString? = JSString("main-thread-owned-key") + _ = string!.asInternalJSRef() + + let box = Box(string!) + string = nil + + // Drop the last reference on a worker — deinit fires on the worker. + // Before the fix this crashed: TypeError: Cannot read properties of undefined (reading 'rc') + let task = Task(executorPreference: executor) { + XCTAssertFalse(isMainThread()) + box.string = nil + } + await task.value + } } #endif diff --git a/Tests/JavaScriptKitTests/JSClosureTests.swift b/Tests/JavaScriptKitTests/JSClosureTests.swift index 3d609a9b9..e278656d8 100644 --- a/Tests/JavaScriptKitTests/JSClosureTests.swift +++ b/Tests/JavaScriptKitTests/JSClosureTests.swift @@ -92,52 +92,38 @@ class JSClosureTests: XCTestCase { throw XCTSkip("Missing --expose-gc flag") } - // Step 1: Create many JSClosure instances + // Step 1: Create many source closures and keep only JS references alive. + // These closures must remain callable even after heavy finalizer churn. let obj = JSObject() - var closurePointers: Set = [] let numberOfSourceClosures = 10_000 do { var closures: [JSClosure] = [] for i in 0.. maxClosurePointer { - break + let numberOfProbeClosures = 50_000 + for i in 0.. { return flag; }, + "jsRoundTripLightColor": (value) => { + return value; + }, "jsEchoJSValue": (v) => { return v; }, @@ -124,12 +130,21 @@ export async function setupOptions(options, context) { if (!exports) { throw new Error("No exports!?"); } - BridgeJSRuntimeTests_runAsyncWorks(exports); + await runAsyncWorksTests(exports); return; }, + AsyncImportImports: getAsyncImportImports(importsContext), + fetchWeatherData: (city) => { + return Promise.resolve({ + temperature: city === "London" ? 15.5 : 25.0, + description: city === "London" ? "Cloudy" : "Sunny", + humidity: city === "London" ? 80 : 40, + }); + }, jsTranslatePoint: (point, dx, dy) => { return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) }; }, + jsRoundTripOptionalPoint: (point) => point, roundTripArrayMembers: (value) => { return value; }, @@ -146,6 +161,8 @@ export async function setupOptions(options, context) { DefaultArgumentImports: getDefaultArgumentImports(importsContext), JSClassSupportImports: getJSClassSupportImports(importsContext), IntegerTypesSupportImports: getIntegerTypesSupportImports(importsContext), + JSTypedArrayImports: getJSTypedArrayImports(importsContext), + IdentityModeTestImports: getIdentityModeTestImports(importsContext), }; }, addToCoreImports(importObject, importsContext) { @@ -615,6 +632,11 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { roundTrippedUUID.release(); uuid.release(); + const uuidFromStatic = exports.__Swift.Foundation.UUID.fromValue("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + assert.equal(uuidFromStatic.uuidString(), "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + uuidFromStatic.release(); + assert.equal(exports.__Swift.Foundation.UUID.placeholder, "00000000-0000-0000-0000-000000000000"); + const createdServer = exports.createHTTPServer(); createdServer.call(exports.Networking.API.Method.Get); createdServer.release(); @@ -1007,11 +1029,17 @@ function testStructSupport(exports) { const fooContainerResult2 = exports.roundTripFooContainer(fooContainer2); assert.equal(fooContainerResult2.foo.value, "first"); assert.equal(fooContainerResult2.optionalFoo, null); -} -/** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ -async function BridgeJSRuntimeTests_runAsyncWorks(exports) { - await exports.asyncRoundTripVoid(); + // Test nested structs with same short name under different parents + const metaA = { label: "hello", count: 42 }; + const metaAResult = exports.NestedStructGroupA.roundtripMetadata(metaA); + assert.equal(metaAResult.label, "hello"); + assert.equal(metaAResult.count, 42); + + const metaB = { tag: "world", value: 3.14 }; + const metaBResult = exports.NestedStructGroupB.roundtripMetadata(metaB); + assert.equal(metaBResult.tag, "world"); + assert.equal(metaBResult.value, 3.14); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ diff --git a/Utilities/bridge-js-generate.sh b/Utilities/bridge-js-generate.sh index 22182d24b..77bdd0833 100755 --- a/Utilities/bridge-js-generate.sh +++ b/Utilities/bridge-js-generate.sh @@ -6,5 +6,6 @@ swift build --package-path ./Plugins/BridgeJS --product BridgeJSTool ./Plugins/BridgeJS/.build/debug/BridgeJSTool generate --project ./tsconfig.json --module-name BridgeJSRuntimeTests --target-dir ./Tests/BridgeJSRuntimeTests --output-dir ./Tests/BridgeJSRuntimeTests/Generated ./Plugins/BridgeJS/.build/debug/BridgeJSTool generate --project ./tsconfig.json --module-name BridgeJSGlobalTests --target-dir ./Tests/BridgeJSGlobalTests --output-dir ./Tests/BridgeJSGlobalTests/Generated +./Plugins/BridgeJS/.build/debug/BridgeJSTool generate --project ./tsconfig.json --module-name BridgeJSIdentityTests --target-dir ./Tests/BridgeJSIdentityTests --output-dir ./Tests/BridgeJSIdentityTests/Generated ./Plugins/BridgeJS/.build/debug/BridgeJSTool generate --project ./tsconfig.json --module-name Benchmarks --target-dir ./Benchmarks/Sources --output-dir ./Benchmarks/Sources/Generated ./Plugins/BridgeJS/.build/debug/BridgeJSTool generate --project ./tsconfig.json --module-name PlayBridgeJS --target-dir ./Examples/PlayBridgeJS/Sources/PlayBridgeJS --output-dir ./Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated diff --git a/Utilities/setup-dev.sh b/Utilities/setup-dev.sh new file mode 100755 index 000000000..261e5ebf6 --- /dev/null +++ b/Utilities/setup-dev.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# +# Set up a local development environment for JavaScriptKit. +# +# Steps: +# 1. Verify required tools are available (swiftly, swift, jq, npm, make, curl). +# 2. If .swift-version is present, ensure that toolchain is installed via swiftly. +# 3. Resolve a matching Wasm SDK from https://github.com/swiftwasm/swift-sdk-index +# and install it (idempotent — skipped if already installed). +# 4. Run `make bootstrap` to install JS dependencies. +# 5. Print the SWIFT_SDK_ID so it can be exported for `make unittest`. +# +# The script runs under bash via the shebang. The final `export` instructions +# it prints work unchanged in both bash and zsh. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +INDEX_BASE="https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1" + +if [[ -t 1 ]]; then + C_BLUE=$'\033[1;34m'; C_YELLOW=$'\033[1;33m'; C_RED=$'\033[1;31m'; C_RESET=$'\033[0m' +else + C_BLUE=''; C_YELLOW=''; C_RED=''; C_RESET='' +fi + +log() { printf '%s==>%s %s\n' "$C_BLUE" "$C_RESET" "$*"; } +warn() { printf '%swarn:%s %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; } +fail() { printf '%serror:%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; exit 1; } + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1${2:+ ($2)}" +} + +log "Checking required tools..." +require_cmd curl +require_cmd jq "install via 'brew install jq' or your package manager" +require_cmd npm "install Node.js from https://nodejs.org" +require_cmd make +require_cmd swiftly "install from https://www.swift.org/install/macos/swiftly" +require_cmd swift "install a Swift toolchain via swiftly" +require_cmd swiftc + +# 1. Honor a .swift-version pin if the repo has one. +if [[ -f .swift-version ]]; then + pinned="$(tr -d '[:space:]' < .swift-version)" + if [[ -n "$pinned" ]]; then + log "Repo pins Swift $pinned via .swift-version" + if ! swiftly list 2>/dev/null | grep -qF "$pinned"; then + log "Installing Swift $pinned via swiftly..." + swiftly install "$pinned" + fi + fi +fi + +SWIFT_VERSION_KEY="$(swiftc --version | head -n1)" +log "Active Swift: $SWIFT_VERSION_KEY" + +# 2. Resolve a matching Wasm SDK. +log "Resolving Wasm SDK from swift-sdk-index..." +TAG_BY_VERSION="$(curl -fsSL "$INDEX_BASE/tag-by-version.json")" +TAG="$(jq -r --arg v "$SWIFT_VERSION_KEY" '.[$v] // [] | .[-1] // empty' <<<"$TAG_BY_VERSION")" + +if [[ -z "$TAG" ]]; then + cat >&2 <'. + + - Use an OSS development snapshot from https://www.swift.org/install/ + +See https://github.com/swiftwasm/swift-sdk-index for details. +EOF + exit 1 +fi + +log "Resolved tag: $TAG" +BUILD_JSON="$(curl -fsSL "$INDEX_BASE/builds/$TAG.json")" +SDK_URL="$(jq -r '."swift-sdks"."wasm32-unknown-wasip1".url' <<<"$BUILD_JSON")" +SDK_CHECKSUM="$(jq -r '."swift-sdks"."wasm32-unknown-wasip1".checksum' <<<"$BUILD_JSON")" +SDK_ID="$(jq -r '."swift-sdks"."wasm32-unknown-wasip1".id' <<<"$BUILD_JSON")" + +if swift sdk list 2>/dev/null | grep -qx "$SDK_ID"; then + log "Wasm SDK already installed: $SDK_ID" +else + log "Installing Wasm SDK: $SDK_ID" + swift sdk install "$SDK_URL" --checksum "$SDK_CHECKSUM" +fi + +# 3. JS dependencies. +log "Installing JS dependencies (make bootstrap)..." +make bootstrap + +cat <