From b962977b8e5060f5bed436350f50330bcaf606e2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 17:51:10 +0900 Subject: [PATCH 1/8] BridgeJS: Skip writing `.actual` files when updating snapshots --- .../Tests/BridgeJSToolTests/SnapshotTesting.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift index 28b34bf6..ce7066a6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift @@ -24,14 +24,15 @@ func assertSnapshot( let existingSnapshot = try String(contentsOf: snapshotPath, encoding: .utf8) let ok = existingSnapshot == String(data: input, encoding: .utf8)! let actualFilePath = snapshotPath.path + ".actual" + let updateSnapshots = ProcessInfo.processInfo.environment["UPDATE_SNAPSHOTS"] != nil func buildComment() -> Comment { "Snapshot mismatch: \(actualFilePath) \(snapshotPath.path)" } - if !ok { - try input.write(to: URL(fileURLWithPath: actualFilePath)) - } - if ProcessInfo.processInfo.environment["UPDATE_SNAPSHOTS"] == nil { + if !updateSnapshots { #expect(ok, buildComment(), sourceLocation: sourceLocation) + if !ok { + try input.write(to: URL(fileURLWithPath: actualFilePath)) + } } else { try input.write(to: snapshotPath) } From 62be4202fa5e559858feee9ba4a922063e6ab3f3 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 17:52:37 +0900 Subject: [PATCH 2/8] BridgeJS: Exclude non-Swift files --- Examples/PlayBridgeJS/Package.swift | 5 +++++ Package.swift | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Examples/PlayBridgeJS/Package.swift b/Examples/PlayBridgeJS/Package.swift index 76e95d1a..258518b4 100644 --- a/Examples/PlayBridgeJS/Package.swift +++ b/Examples/PlayBridgeJS/Package.swift @@ -22,6 +22,11 @@ let package = Package( .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), ], + exclude: [ + "bridge-js.d.ts", + "bridge-js.config.json", + "Generated/JavaScript", + ], swiftSettings: [ .enableExperimentalFeature("Extern") ], diff --git a/Package.swift b/Package.swift index f8b3b6e9..30ae0e88 100644 --- a/Package.swift +++ b/Package.swift @@ -156,7 +156,11 @@ let package = Package( .testTarget( name: "BridgeJSRuntimeTests", dependencies: ["JavaScriptKit"], - exclude: ["Generated/JavaScript"], + exclude: [ + "bridge-js.config.json", + "bridge-js.d.ts", + "Generated/JavaScript" + ], swiftSettings: [ .enableExperimentalFeature("Extern") ] From 58fce8c1b49ae1f10046951c4529d011624fdb5c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 17:55:27 +0900 Subject: [PATCH 3/8] BridgeJS: Suppress warning about unhandled resource files --- Package.swift | 5 ++-- .../Sources/BridgeJSCore/ImportTS.swift | 3 +++ .../ImportTSTests/ArrayParameter.swift | 12 +++++----- .../ImportTSTests/Interface.swift | 12 +++++----- .../ImportTSTests/PrimitiveParameters.swift | 4 ++-- .../ImportTSTests/PrimitiveReturn.swift | 8 +++---- .../ImportTSTests/StringParameter.swift | 8 +++---- .../ImportTSTests/StringReturn.swift | 4 ++-- .../ImportTSTests/TypeAlias.swift | 4 ++-- .../ImportTSTests/TypeScriptClass.swift | 24 +++++++++---------- .../VoidParameterVoidReturn.swift | 4 ++-- 11 files changed, 46 insertions(+), 42 deletions(-) diff --git a/Package.swift b/Package.swift index 30ae0e88..435ae1a1 100644 --- a/Package.swift +++ b/Package.swift @@ -151,7 +151,8 @@ let package = Package( .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), ], - path: "Plugins/BridgeJS/Sources/BridgeJSTool" + path: "Plugins/BridgeJS/Sources/BridgeJSTool", + exclude: ["TS2Skeleton/JavaScript"] ), .testTarget( name: "BridgeJSRuntimeTests", @@ -159,7 +160,7 @@ let package = Package( exclude: [ "bridge-js.config.json", "bridge-js.d.ts", - "Generated/JavaScript" + "Generated/JavaScript", ], swiftSettings: [ .enableExperimentalFeature("Extern") diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 4aef01fd..549cbf9f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -213,6 +213,9 @@ struct ImportTS { ) } }), + effectSpecifiers: FunctionEffectSpecifiersSyntax( + throwsClause: ThrowsClauseSyntax(throwsSpecifier: .keyword(.throws)) + ), returnClause: ReturnClauseSyntax( arrow: .arrowToken(), type: IdentifierTypeSyntax(name: .identifier(abiReturnType.map { $0.swiftType } ?? "Void")) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift index 0a85c88f..dfcba823 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift @@ -9,9 +9,9 @@ func checkArray(_ a: JSObject) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArray") - func bjs_checkArray(_ a: Int32) -> Void + func bjs_checkArray(_ a: Int32) throws -> Void #else - func bjs_checkArray(_ a: Int32) -> Void { + func bjs_checkArray(_ a: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif @@ -21,9 +21,9 @@ func checkArray(_ a: JSObject) -> Void { func checkArrayWithLength(_ a: JSObject, _ b: Double) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArrayWithLength") - func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void + func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) throws -> Void #else - func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void { + func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) throws -> Void { fatalError("Only available on WebAssembly") } #endif @@ -33,9 +33,9 @@ func checkArrayWithLength(_ a: JSObject, _ b: Double) -> Void { func checkArray(_ a: JSObject) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArray") - func bjs_checkArray(_ a: Int32) -> Void + func bjs_checkArray(_ a: Int32) throws -> Void #else - func bjs_checkArray(_ a: Int32) -> Void { + func bjs_checkArray(_ a: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift index 44fbb6ba..bdb7d4d3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift @@ -9,9 +9,9 @@ func returnAnimatable() -> Animatable { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_returnAnimatable") - func bjs_returnAnimatable() -> Int32 + func bjs_returnAnimatable() throws -> Int32 #else - func bjs_returnAnimatable() -> Int32 { + func bjs_returnAnimatable() throws -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -33,9 +33,9 @@ struct Animatable { func animate(_ keyframes: JSObject, _ options: JSObject) -> JSObject { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Animatable_animate") - func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32 + func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) throws -> Int32 #else - func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32 { + func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) throws -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -46,9 +46,9 @@ struct Animatable { func getAnimations(_ options: JSObject) -> JSObject { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Animatable_getAnimations") - func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32 + func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) throws -> Int32 #else - func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32 { + func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) throws -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift index c5d23884..219cd893 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift @@ -9,9 +9,9 @@ func check(_ a: Double, _ b: Bool) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_check") - func bjs_check(_ a: Float64, _ b: Int32) -> Void + func bjs_check(_ a: Float64, _ b: Int32) throws -> Void #else - func bjs_check(_ a: Float64, _ b: Int32) -> Void { + func bjs_check(_ a: Float64, _ b: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift index df1a469a..69f6014c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift @@ -9,9 +9,9 @@ func checkNumber() -> Double { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkNumber") - func bjs_checkNumber() -> Float64 + func bjs_checkNumber() throws -> Float64 #else - func bjs_checkNumber() -> Float64 { + func bjs_checkNumber() throws -> Float64 { fatalError("Only available on WebAssembly") } #endif @@ -22,9 +22,9 @@ func checkNumber() -> Double { func checkBoolean() -> Bool { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkBoolean") - func bjs_checkBoolean() -> Int32 + func bjs_checkBoolean() throws -> Int32 #else - func bjs_checkBoolean() -> Int32 { + func bjs_checkBoolean() throws -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift index 8cac3ce6..366ff65f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift @@ -9,9 +9,9 @@ func checkString(_ a: String) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkString") - func bjs_checkString(_ a: Int32) -> Void + func bjs_checkString(_ a: Int32) throws -> Void #else - func bjs_checkString(_ a: Int32) -> Void { + func bjs_checkString(_ a: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif @@ -25,9 +25,9 @@ func checkString(_ a: String) -> Void { func checkStringWithLength(_ a: String, _ b: Double) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkStringWithLength") - func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void + func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) throws -> Void #else - func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void { + func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) throws -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift index 282476b0..4aaf605d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift @@ -9,9 +9,9 @@ func checkString() -> String { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkString") - func bjs_checkString() -> Int32 + func bjs_checkString() throws -> Int32 #else - func bjs_checkString() -> Int32 { + func bjs_checkString() throws -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift index 7eeefcc7..07d9ebea 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift @@ -9,9 +9,9 @@ func checkSimple(_ a: Double) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkSimple") - func bjs_checkSimple(_ a: Float64) -> Void + func bjs_checkSimple(_ a: Float64) throws -> Void #else - func bjs_checkSimple(_ a: Float64) -> Void { + func bjs_checkSimple(_ a: Float64) throws -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift index 39b6a85c..aa7e57df 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift @@ -20,9 +20,9 @@ struct Greeter { init(_ name: String) { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_init") - func bjs_Greeter_init(_ name: Int32) -> Int32 + func bjs_Greeter_init(_ name: Int32) throws -> Int32 #else - func bjs_Greeter_init(_ name: Int32) -> Int32 { + func bjs_Greeter_init(_ name: Int32) throws -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -38,9 +38,9 @@ struct Greeter { get { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_name_get") - func bjs_Greeter_name_get(_ self: Int32) -> Int32 + func bjs_Greeter_name_get(_ self: Int32) throws -> Int32 #else - func bjs_Greeter_name_get(_ self: Int32) -> Int32 { + func bjs_Greeter_name_get(_ self: Int32) throws -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -53,9 +53,9 @@ struct Greeter { nonmutating set { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_name_set") - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) throws -> Void #else - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif @@ -71,9 +71,9 @@ struct Greeter { get { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_age_get") - func bjs_Greeter_age_get(_ self: Int32) -> Float64 + func bjs_Greeter_age_get(_ self: Int32) throws -> Float64 #else - func bjs_Greeter_age_get(_ self: Int32) -> Float64 { + func bjs_Greeter_age_get(_ self: Int32) throws -> Float64 { fatalError("Only available on WebAssembly") } #endif @@ -85,9 +85,9 @@ struct Greeter { func greet() -> String { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_greet") - func bjs_Greeter_greet(_ self: Int32) -> Int32 + func bjs_Greeter_greet(_ self: Int32) throws -> Int32 #else - func bjs_Greeter_greet(_ self: Int32) -> Int32 { + func bjs_Greeter_greet(_ self: Int32) throws -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -101,9 +101,9 @@ struct Greeter { func changeName(_ name: String) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_changeName") - func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void + func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) throws -> Void #else - func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void { + func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) throws -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift index eccd1728..fda11d5c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift @@ -9,9 +9,9 @@ func check() -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_check") - func bjs_check() -> Void + func bjs_check() throws -> Void #else - func bjs_check() -> Void { + func bjs_check() throws -> Void { fatalError("Only available on WebAssembly") } #endif From 1a83f7277c9438db623b9bae3775f43da45b2fdb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 18:12:24 +0900 Subject: [PATCH 4/8] BridgeJS: Add `throws(JSException)` to imported methods --- .../Sources/Generated/BridgeJS.ImportTS.swift | 6 ++-- .../Generated/BridgeJS.ExportSwift.swift | 6 ++-- .../Generated/BridgeJS.ImportTS.swift | 4 +-- .../Sources/PlayBridgeJS/main.swift | 6 ++-- .../Sources/BridgeJSCore/ImportTS.swift | 17 +++++++++-- .../ImportTSTests/ArrayParameter.swift | 18 ++++++------ .../ImportTSTests/Interface.swift | 18 ++++++------ .../ImportTSTests/PrimitiveParameters.swift | 6 ++-- .../ImportTSTests/PrimitiveReturn.swift | 12 ++++---- .../ImportTSTests/StringParameter.swift | 12 ++++---- .../ImportTSTests/StringReturn.swift | 6 ++-- .../ImportTSTests/TypeAlias.swift | 6 ++-- .../ImportTSTests/TypeScriptClass.swift | 28 +++++++++---------- .../VoidParameterVoidReturn.swift | 6 ++-- .../Generated/BridgeJS.ExportSwift.swift | 14 +++++----- .../Generated/BridgeJS.ImportTS.swift | 12 ++++---- 16 files changed, 95 insertions(+), 82 deletions(-) diff --git a/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift b/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift index 53f06222..e8aa53b4 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -func benchmarkHelperNoop() -> Void { +func benchmarkHelperNoop() throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoop") func bjs_benchmarkHelperNoop() -> Void @@ -18,7 +18,7 @@ func benchmarkHelperNoop() -> Void { bjs_benchmarkHelperNoop() } -func benchmarkHelperNoopWithNumber(_ n: Double) -> Void { +func benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoopWithNumber") func bjs_benchmarkHelperNoopWithNumber(_ n: Float64) -> Void @@ -30,7 +30,7 @@ func benchmarkHelperNoopWithNumber(_ n: Double) -> Void { bjs_benchmarkHelperNoopWithNumber(n) } -func benchmarkRunner(_ name: String, _ body: JSObject) -> Void { +func benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner") func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift index 283a3fbb..b0656df9 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift @@ -26,12 +26,12 @@ public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSource _swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped) return Int(swiftSourceLen) } - let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in + let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in _swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped) return Int(dtsSourceLen) } - let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) - return Unmanaged.passRetained(ret).toOpaque() + let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) + return Unmanaged.passRetained(ret).toOpaque() } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift index 6698cab5..4f7c4865 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -func createTS2Skeleton() -> TS2Skeleton { +func createTS2Skeleton() throws(JSException) -> TS2Skeleton { #if arch(wasm32) @_extern(wasm, module: "PlayBridgeJS", name: "bjs_createTS2Skeleton") func bjs_createTS2Skeleton() -> Int32 @@ -30,7 +30,7 @@ struct TS2Skeleton { self.this = JSObject(id: UInt32(bitPattern: this)) } - func convert(_ ts: String) -> String { + func convert(_ ts: String) throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "PlayBridgeJS", name: "bjs_TS2Skeleton_convert") func bjs_TS2Skeleton_convert(_ self: Int32, _ ts: Int32) -> Int32 diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift index 7dc5a57f..8f224994 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift @@ -9,6 +9,8 @@ import class Foundation.JSONDecoder @JS func update(swiftSource: String, dtsSource: String) throws(JSException) -> PlayBridgeJSOutput { do { return try _update(swiftSource: swiftSource, dtsSource: dtsSource) + } catch let error as JSException { + throw error } catch { throw JSException(message: String(describing: error)) } @@ -20,8 +22,8 @@ import class Foundation.JSONDecoder try exportSwift.addSourceFile(sourceFile, "Playground.swift") let exportResult = try exportSwift.finalize() var importTS = ImportTS(progress: .silent, moduleName: "Playground") - let ts2skeleton = createTS2Skeleton() - let skeletonJSONString = ts2skeleton.convert(dtsSource) + let ts2skeleton = try createTS2Skeleton() + let skeletonJSONString = try ts2skeleton.convert(dtsSource) let decoder = JSONDecoder() let importSkeleton = try decoder.decode( ImportedFileSkeleton.self, diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 549cbf9f..86aba5dc 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -213,9 +213,6 @@ struct ImportTS { ) } }), - effectSpecifiers: FunctionEffectSpecifiersSyntax( - throwsClause: ThrowsClauseSyntax(throwsSpecifier: .keyword(.throws)) - ), returnClause: ReturnClauseSyntax( arrow: .arrowToken(), type: IdentifierTypeSyntax(name: .identifier(abiReturnType.map { $0.swiftType } ?? "Void")) @@ -256,6 +253,7 @@ struct ImportTS { ) } }), + effectSpecifiers: ImportTS.buildFunctionEffect(throws: true, async: false), returnClause: ReturnClauseSyntax( arrow: .arrowToken(), type: IdentifierTypeSyntax(name: .identifier(returnType.swiftType)) @@ -472,4 +470,17 @@ struct ImportTS { let lines = documentation.split { $0.isNewline } return Trivia(pieces: lines.flatMap { [TriviaPiece.docLineComment("/// \($0)"), .newlines(1)] }) } + + static func buildFunctionEffect(throws: Bool, async: Bool) -> FunctionEffectSpecifiersSyntax { + return FunctionEffectSpecifiersSyntax( + asyncSpecifier: `async` ? .keyword(.async) : nil, + throwsClause: `throws` + ? ThrowsClauseSyntax( + throwsSpecifier: .keyword(.throws), + leftParen: .leftParenToken(), + type: IdentifierTypeSyntax(name: .identifier("JSException")), + rightParen: .rightParenToken() + ) : nil, + ) + } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift index dfcba823..9faf6209 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift @@ -6,36 +6,36 @@ @_spi(BridgeJS) import JavaScriptKit -func checkArray(_ a: JSObject) -> Void { +func checkArray(_ a: JSObject) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArray") - func bjs_checkArray(_ a: Int32) throws -> Void + func bjs_checkArray(_ a: Int32) -> Void #else - func bjs_checkArray(_ a: Int32) throws -> Void { + func bjs_checkArray(_ a: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif bjs_checkArray(Int32(bitPattern: a.id)) } -func checkArrayWithLength(_ a: JSObject, _ b: Double) -> Void { +func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArrayWithLength") - func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) throws -> Void + func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void #else - func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) throws -> Void { + func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif bjs_checkArrayWithLength(Int32(bitPattern: a.id), b) } -func checkArray(_ a: JSObject) -> Void { +func checkArray(_ a: JSObject) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkArray") - func bjs_checkArray(_ a: Int32) throws -> Void + func bjs_checkArray(_ a: Int32) -> Void #else - func bjs_checkArray(_ a: Int32) throws -> Void { + func bjs_checkArray(_ a: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift index bdb7d4d3..8968a9f3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func returnAnimatable() -> Animatable { +func returnAnimatable() throws(JSException) -> Animatable { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_returnAnimatable") - func bjs_returnAnimatable() throws -> Int32 + func bjs_returnAnimatable() -> Int32 #else - func bjs_returnAnimatable() throws -> Int32 { + func bjs_returnAnimatable() -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -30,12 +30,12 @@ struct Animatable { self.this = JSObject(id: UInt32(bitPattern: this)) } - func animate(_ keyframes: JSObject, _ options: JSObject) -> JSObject { + func animate(_ keyframes: JSObject, _ options: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Animatable_animate") - func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) throws -> Int32 + func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32 #else - func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) throws -> Int32 { + func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -43,12 +43,12 @@ struct Animatable { return JSObject(id: UInt32(bitPattern: ret)) } - func getAnimations(_ options: JSObject) -> JSObject { + func getAnimations(_ options: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Animatable_getAnimations") - func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) throws -> Int32 + func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32 #else - func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) throws -> Int32 { + func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift index 219cd893..afef0de3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func check(_ a: Double, _ b: Bool) -> Void { +func check(_ a: Double, _ b: Bool) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_check") - func bjs_check(_ a: Float64, _ b: Int32) throws -> Void + func bjs_check(_ a: Float64, _ b: Int32) -> Void #else - func bjs_check(_ a: Float64, _ b: Int32) throws -> Void { + func bjs_check(_ a: Float64, _ b: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift index 69f6014c..11bd7d4f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func checkNumber() -> Double { +func checkNumber() throws(JSException) -> Double { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkNumber") - func bjs_checkNumber() throws -> Float64 + func bjs_checkNumber() -> Float64 #else - func bjs_checkNumber() throws -> Float64 { + func bjs_checkNumber() -> Float64 { fatalError("Only available on WebAssembly") } #endif @@ -19,12 +19,12 @@ func checkNumber() -> Double { return Double(ret) } -func checkBoolean() -> Bool { +func checkBoolean() throws(JSException) -> Bool { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkBoolean") - func bjs_checkBoolean() throws -> Int32 + func bjs_checkBoolean() -> Int32 #else - func bjs_checkBoolean() throws -> Int32 { + func bjs_checkBoolean() -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift index 366ff65f..fb783de1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func checkString(_ a: String) -> Void { +func checkString(_ a: String) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkString") - func bjs_checkString(_ a: Int32) throws -> Void + func bjs_checkString(_ a: Int32) -> Void #else - func bjs_checkString(_ a: Int32) throws -> Void { + func bjs_checkString(_ a: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif @@ -22,12 +22,12 @@ func checkString(_ a: String) -> Void { bjs_checkString(aId) } -func checkStringWithLength(_ a: String, _ b: Double) -> Void { +func checkStringWithLength(_ a: String, _ b: Double) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkStringWithLength") - func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) throws -> Void + func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void #else - func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) throws -> Void { + func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift index 4aaf605d..fd26de22 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func checkString() -> String { +func checkString() throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkString") - func bjs_checkString() throws -> Int32 + func bjs_checkString() -> Int32 #else - func bjs_checkString() throws -> Int32 { + func bjs_checkString() -> Int32 { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift index 07d9ebea..a8523110 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func checkSimple(_ a: Double) -> Void { +func checkSimple(_ a: Double) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_checkSimple") - func bjs_checkSimple(_ a: Float64) throws -> Void + func bjs_checkSimple(_ a: Float64) -> Void #else - func bjs_checkSimple(_ a: Float64) throws -> Void { + func bjs_checkSimple(_ a: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift index aa7e57df..53e1899b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift @@ -20,9 +20,9 @@ struct Greeter { init(_ name: String) { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_init") - func bjs_Greeter_init(_ name: Int32) throws -> Int32 + func bjs_Greeter_init(_ name: Int32) -> Int32 #else - func bjs_Greeter_init(_ name: Int32) throws -> Int32 { + func bjs_Greeter_init(_ name: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -38,9 +38,9 @@ struct Greeter { get { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_name_get") - func bjs_Greeter_name_get(_ self: Int32) throws -> Int32 + func bjs_Greeter_name_get(_ self: Int32) -> Int32 #else - func bjs_Greeter_name_get(_ self: Int32) throws -> Int32 { + func bjs_Greeter_name_get(_ self: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -53,9 +53,9 @@ struct Greeter { nonmutating set { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_name_set") - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) throws -> Void + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void #else - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) throws -> Void { + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif @@ -71,9 +71,9 @@ struct Greeter { get { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_age_get") - func bjs_Greeter_age_get(_ self: Int32) throws -> Float64 + func bjs_Greeter_age_get(_ self: Int32) -> Float64 #else - func bjs_Greeter_age_get(_ self: Int32) throws -> Float64 { + func bjs_Greeter_age_get(_ self: Int32) -> Float64 { fatalError("Only available on WebAssembly") } #endif @@ -82,12 +82,12 @@ struct Greeter { } } - func greet() -> String { + func greet() throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_greet") - func bjs_Greeter_greet(_ self: Int32) throws -> Int32 + func bjs_Greeter_greet(_ self: Int32) -> Int32 #else - func bjs_Greeter_greet(_ self: Int32) throws -> Int32 { + func bjs_Greeter_greet(_ self: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif @@ -98,12 +98,12 @@ struct Greeter { } } - func changeName(_ name: String) -> Void { + func changeName(_ name: String) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_changeName") - func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) throws -> Void + func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void #else - func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) throws -> Void { + func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift index fda11d5c..7c2b3e14 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift @@ -6,12 +6,12 @@ @_spi(BridgeJS) import JavaScriptKit -func check() -> Void { +func check() throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_check") - func bjs_check() throws -> Void + func bjs_check() -> Void #else - func bjs_check() throws -> Void { + func bjs_check() -> Void { fatalError("Only available on WebAssembly") } #endif diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 6c11869f..2a91da9f 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -129,7 +129,7 @@ public func _bjs_throwsWithIntResult() -> Int32 { #if arch(wasm32) do { let ret = try throwsWithIntResult() - return Int32(ret) + return Int32(ret) } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { @@ -154,7 +154,7 @@ public func _bjs_throwsWithStringResult() -> Void { #if arch(wasm32) do { var ret = try throwsWithStringResult() - return ret.withUTF8 { ptr in + return ret.withUTF8 { ptr in _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) } } catch let error { @@ -181,7 +181,7 @@ public func _bjs_throwsWithBoolResult() -> Int32 { #if arch(wasm32) do { let ret = try throwsWithBoolResult() - return Int32(ret ? 1 : 0) + return Int32(ret ? 1 : 0) } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { @@ -206,7 +206,7 @@ public func _bjs_throwsWithFloatResult() -> Float32 { #if arch(wasm32) do { let ret = try throwsWithFloatResult() - return Float32(ret) + return Float32(ret) } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { @@ -231,7 +231,7 @@ public func _bjs_throwsWithDoubleResult() -> Float64 { #if arch(wasm32) do { let ret = try throwsWithDoubleResult() - return Float64(ret) + return Float64(ret) } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { @@ -256,7 +256,7 @@ public func _bjs_throwsWithSwiftHeapObjectResult() -> UnsafeMutableRawPointer { #if arch(wasm32) do { let ret = try throwsWithSwiftHeapObjectResult() - return Unmanaged.passRetained(ret).toOpaque() + return Unmanaged.passRetained(ret).toOpaque() } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { @@ -281,7 +281,7 @@ public func _bjs_throwsWithJSObjectResult() -> Int32 { #if arch(wasm32) do { let ret = try throwsWithJSObjectResult() - return _swift_js_retain(Int32(bitPattern: ret.id)) + return _swift_js_retain(Int32(bitPattern: ret.id)) } catch let error { if let error = error.thrownValue.object { withExtendedLifetime(error) { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift index 95150e0e..ee8a6d3e 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -func jsRoundTripVoid() -> Void { +func jsRoundTripVoid() throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripVoid") func bjs_jsRoundTripVoid() -> Void @@ -18,7 +18,7 @@ func jsRoundTripVoid() -> Void { bjs_jsRoundTripVoid() } -func jsRoundTripNumber(_ v: Double) -> Double { +func jsRoundTripNumber(_ v: Double) throws(JSException) -> Double { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripNumber") func bjs_jsRoundTripNumber(_ v: Float64) -> Float64 @@ -31,7 +31,7 @@ func jsRoundTripNumber(_ v: Double) -> Double { return Double(ret) } -func jsRoundTripBool(_ v: Bool) -> Bool { +func jsRoundTripBool(_ v: Bool) throws(JSException) -> Bool { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripBool") func bjs_jsRoundTripBool(_ v: Int32) -> Int32 @@ -44,7 +44,7 @@ func jsRoundTripBool(_ v: Bool) -> Bool { return ret == 1 } -func jsRoundTripString(_ v: String) -> String { +func jsRoundTripString(_ v: String) throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripString") func bjs_jsRoundTripString(_ v: Int32) -> Int32 @@ -147,7 +147,7 @@ struct JsGreeter { } } - func greet() -> String { + func greet() throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_greet") func bjs_JsGreeter_greet(_ self: Int32) -> Int32 @@ -163,7 +163,7 @@ struct JsGreeter { } } - func changeName(_ name: String) -> Void { + func changeName(_ name: String) throws(JSException) -> Void { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_changeName") func bjs_JsGreeter_changeName(_ self: Int32, _ name: Int32) -> Void From 8df0593bb2f172f3c090cbba98f2e00e6c398359 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 18:37:20 +0900 Subject: [PATCH 5/8] BridgeJS: Split setter into a separate function To mark it `throws` --- .../Generated/BridgeJS.ExportSwift.swift | 100 +++++++++--------- .../JavaScript/BridgeJS.ExportSwift.json | 96 ++++++++--------- .../Sources/BridgeJSCore/ImportTS.swift | 51 ++++++--- .../ImportTSTests/TypeScriptClass.swift | 35 +++--- .../Generated/BridgeJS.ImportTS.swift | 35 +++--- .../BridgeJSRuntimeTests/ImportAPITests.swift | 36 +++---- 6 files changed, 187 insertions(+), 166 deletions(-) diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift index b0656df9..ce93f95b 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift @@ -6,56 +6,6 @@ @_spi(BridgeJS) import JavaScriptKit -@_expose(wasm, "bjs_PlayBridgeJS_init") -@_cdecl("bjs_PlayBridgeJS_init") -public func _bjs_PlayBridgeJS_init() -> UnsafeMutableRawPointer { - #if arch(wasm32) - let ret = PlayBridgeJS() - return Unmanaged.passRetained(ret).toOpaque() - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PlayBridgeJS_update") -@_cdecl("bjs_PlayBridgeJS_update") -public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSourceBytes: Int32, swiftSourceLen: Int32, dtsSourceBytes: Int32, dtsSourceLen: Int32) -> UnsafeMutableRawPointer { - #if arch(wasm32) - do { - let swiftSource = String(unsafeUninitializedCapacity: Int(swiftSourceLen)) { b in - _swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped) - return Int(swiftSourceLen) - } - let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in - _swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped) - return Int(dtsSourceLen) - } - let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) - return Unmanaged.passRetained(ret).toOpaque() - } catch let error { - if let error = error.thrownValue.object { - withExtendedLifetime(error) { - _swift_js_throw(Int32(bitPattern: $0.id)) - } - } else { - let jsError = JSError(message: String(describing: error)) - withExtendedLifetime(jsError.jsObject) { - _swift_js_throw(Int32(bitPattern: $0.id)) - } - } - return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped - } - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PlayBridgeJS_deinit") -@_cdecl("bjs_PlayBridgeJS_deinit") -public func _bjs_PlayBridgeJS_deinit(pointer: UnsafeMutableRawPointer) { - Unmanaged.fromOpaque(pointer).release() -} - @_expose(wasm, "bjs_PlayBridgeJSOutput_outputJs") @_cdecl("bjs_PlayBridgeJSOutput_outputJs") public func _bjs_PlayBridgeJSOutput_outputJs(_self: UnsafeMutableRawPointer) -> Void { @@ -112,4 +62,54 @@ public func _bjs_PlayBridgeJSOutput_exportSwiftGlue(_self: UnsafeMutableRawPoint @_cdecl("bjs_PlayBridgeJSOutput_deinit") public func _bjs_PlayBridgeJSOutput_deinit(pointer: UnsafeMutableRawPointer) { Unmanaged.fromOpaque(pointer).release() +} + +@_expose(wasm, "bjs_PlayBridgeJS_init") +@_cdecl("bjs_PlayBridgeJS_init") +public func _bjs_PlayBridgeJS_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = PlayBridgeJS() + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PlayBridgeJS_update") +@_cdecl("bjs_PlayBridgeJS_update") +public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSourceBytes: Int32, swiftSourceLen: Int32, dtsSourceBytes: Int32, dtsSourceLen: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + do { + let swiftSource = String(unsafeUninitializedCapacity: Int(swiftSourceLen)) { b in + _swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped) + return Int(swiftSourceLen) + } + let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in + _swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped) + return Int(dtsSourceLen) + } + let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) + return Unmanaged.passRetained(ret).toOpaque() + } catch let error { + if let error = error.thrownValue.object { + withExtendedLifetime(error) { + _swift_js_throw(Int32(bitPattern: $0.id)) + } + } else { + let jsError = JSError(message: String(describing: error)) + withExtendedLifetime(jsError.jsObject) { + _swift_js_throw(Int32(bitPattern: $0.id)) + } + } + return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PlayBridgeJS_deinit") +@_cdecl("bjs_PlayBridgeJS_deinit") +public func _bjs_PlayBridgeJS_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() } \ No newline at end of file diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json index c4d55d27..910ee14a 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -1,53 +1,5 @@ { "classes" : [ - { - "constructor" : { - "abiName" : "bjs_PlayBridgeJS_init", - "effects" : { - "isAsync" : false, - "isThrows" : false - }, - "parameters" : [ - - ] - }, - "methods" : [ - { - "abiName" : "bjs_PlayBridgeJS_update", - "effects" : { - "isAsync" : false, - "isThrows" : true - }, - "name" : "update", - "parameters" : [ - { - "label" : "swiftSource", - "name" : "swiftSource", - "type" : { - "string" : { - - } - } - }, - { - "label" : "dtsSource", - "name" : "dtsSource", - "type" : { - "string" : { - - } - } - } - ], - "returnType" : { - "swiftHeapObject" : { - "_0" : "PlayBridgeJSOutput" - } - } - } - ], - "name" : "PlayBridgeJS" - }, { "methods" : [ { @@ -116,6 +68,54 @@ } ], "name" : "PlayBridgeJSOutput" + }, + { + "constructor" : { + "abiName" : "bjs_PlayBridgeJS_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_PlayBridgeJS_update", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "update", + "parameters" : [ + { + "label" : "swiftSource", + "name" : "swiftSource", + "type" : { + "string" : { + + } + } + }, + { + "label" : "dtsSource", + "name" : "dtsSource", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "PlayBridgeJSOutput" + } + } + } + ], + "name" : "PlayBridgeJS" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 86aba5dc..d9343399 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -281,7 +281,8 @@ struct ImportTS { ) } } - ) + ), + effectSpecifiers: ImportTS.buildFunctionEffect(throws: true, async: false) ), bodyBuilder: { self.renderImportDecl() @@ -365,6 +366,7 @@ struct ImportTS { try builder.liftReturnValue(returnType: property.type) return AccessorDeclSyntax( accessorSpecifier: .keyword(.get), + effectSpecifiers: Self.buildAccessorEffect(throws: true, async: false), body: CodeBlockSyntax { builder.renderImportDecl() builder.body @@ -372,31 +374,25 @@ struct ImportTS { ) } - func renderSetterDecl(property: ImportedPropertySkeleton) throws -> AccessorDeclSyntax { + func renderSetterDecl(property: ImportedPropertySkeleton) throws -> DeclSyntax { let builder = ImportedThunkBuilder( moduleName: moduleName, abiName: property.setterAbiName(context: type) ) + let newValue = Parameter(label: nil, name: "newValue", type: property.type) try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name))) - try builder.lowerParameter(param: Parameter(label: nil, name: "newValue", type: property.type)) + try builder.lowerParameter(param: newValue) builder.call(returnType: .void) - return AccessorDeclSyntax( - modifier: DeclModifierSyntax(name: .keyword(.nonmutating)), - accessorSpecifier: .keyword(.set), - body: CodeBlockSyntax { - builder.renderImportDecl() - builder.body - } + return builder.renderThunkDecl( + name: "set\(property.name.capitalizedFirstLetter())", + parameters: [newValue], + returnType: .void ) } func renderPropertyDecl(property: ImportedPropertySkeleton) throws -> [DeclSyntax] { - var accessorDecls: [AccessorDeclSyntax] = [] - accessorDecls.append(try renderGetterDecl(property: property)) - if !property.isReadonly { - accessorDecls.append(try renderSetterDecl(property: property)) - } - return [ + let accessorDecls: [AccessorDeclSyntax] = [try renderGetterDecl(property: property)] + var decls: [DeclSyntax] = [ DeclSyntax( VariableDeclSyntax( leadingTrivia: Self.renderDocumentation(documentation: property.documentation), @@ -419,6 +415,10 @@ struct ImportTS { ) ) ] + if !property.isReadonly { + decls.append(try renderSetterDecl(property: property)) + } + return decls } let classDecl = try StructDeclSyntax( leadingTrivia: Self.renderDocumentation(documentation: type.documentation), @@ -483,4 +483,23 @@ struct ImportTS { ) : nil, ) } + static func buildAccessorEffect(throws: Bool, async: Bool) -> AccessorEffectSpecifiersSyntax { + return AccessorEffectSpecifiersSyntax( + asyncSpecifier: `async` ? .keyword(.async) : nil, + throwsClause: `throws` + ? ThrowsClauseSyntax( + throwsSpecifier: .keyword(.throws), + leftParen: .leftParenToken(), + type: IdentifierTypeSyntax(name: .identifier("JSException")), + rightParen: .rightParenToken() + ) : nil, + ) + } +} + +extension String { + func capitalizedFirstLetter() -> String { + guard !isEmpty else { return self } + return prefix(1).uppercased() + dropFirst() + } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift index 53e1899b..d20b2a9c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift @@ -17,7 +17,7 @@ struct Greeter { self.this = JSObject(id: UInt32(bitPattern: this)) } - init(_ name: String) { + init(_ name: String) throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_init") func bjs_Greeter_init(_ name: Int32) -> Int32 @@ -35,7 +35,7 @@ struct Greeter { } var name: String { - get { + get throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_name_get") func bjs_Greeter_name_get(_ self: Int32) -> Int32 @@ -50,25 +50,26 @@ struct Greeter { return Int(ret) } } - nonmutating set { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_Greeter_name_set") - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void - #else - func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { - fatalError("Only available on WebAssembly") - } - #endif - var newValue = newValue - let newValueId = newValue.withUTF8 { b in - _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) - } - bjs_Greeter_name_set(Int32(bitPattern: self.this.id), newValueId) + } + + func setName(_ newValue: String) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_Greeter_name_set") + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void + #else + func bjs_Greeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + var newValue = newValue + let newValueId = newValue.withUTF8 { b in + _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } + bjs_Greeter_name_set(Int32(bitPattern: self.this.id), newValueId) } var age: Double { - get { + get throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "Check", name: "bjs_Greeter_age_get") func bjs_Greeter_age_get(_ self: Int32) -> Float64 diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift index ee8a6d3e..0122fe7e 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift @@ -75,7 +75,7 @@ struct JsGreeter { self.this = JSObject(id: UInt32(bitPattern: this)) } - init(_ name: String, _ prefix: String) { + init(_ name: String, _ prefix: String) throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_init") func bjs_JsGreeter_init(_ name: Int32, _ prefix: Int32) -> Int32 @@ -97,7 +97,7 @@ struct JsGreeter { } var name: String { - get { + get throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_name_get") func bjs_JsGreeter_name_get(_ self: Int32) -> Int32 @@ -112,25 +112,26 @@ struct JsGreeter { return Int(ret) } } - nonmutating set { - #if arch(wasm32) - @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_name_set") - func bjs_JsGreeter_name_set(_ self: Int32, _ newValue: Int32) -> Void - #else - func bjs_JsGreeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { - fatalError("Only available on WebAssembly") - } - #endif - var newValue = newValue - let newValueId = newValue.withUTF8 { b in - _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) - } - bjs_JsGreeter_name_set(Int32(bitPattern: self.this.id), newValueId) + } + + func setName(_ newValue: String) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_name_set") + func bjs_JsGreeter_name_set(_ self: Int32, _ newValue: Int32) -> Void + #else + func bjs_JsGreeter_name_set(_ self: Int32, _ newValue: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + var newValue = newValue + let newValueId = newValue.withUTF8 { b in + _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } + bjs_JsGreeter_name_set(Int32(bitPattern: self.this.id), newValueId) } var prefix: String { - get { + get throws(JSException) { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_JsGreeter_prefix_get") func bjs_JsGreeter_prefix_get(_ self: Int32) -> Int32 diff --git a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift index a8d586bf..ad7b64f4 100644 --- a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift @@ -2,11 +2,11 @@ import XCTest import JavaScriptKit class ImportAPITests: XCTestCase { - func testRoundTripVoid() { - jsRoundTripVoid() + func testRoundTripVoid() throws { + try jsRoundTripVoid() } - func testRoundTripNumber() { + func testRoundTripNumber() throws { for v in [ 0, 1, -1, Double(Int32.max), Double(Int32.min), @@ -17,34 +17,34 @@ class ImportAPITests: XCTestCase { Double.infinity, Double.pi, ] { - XCTAssertEqual(jsRoundTripNumber(v), v) + try XCTAssertEqual(jsRoundTripNumber(v), v) } - XCTAssert(jsRoundTripNumber(Double.nan).isNaN) + try XCTAssert(jsRoundTripNumber(Double.nan).isNaN) } - func testRoundTripBool() { + func testRoundTripBool() throws { for v in [true, false] { - XCTAssertEqual(jsRoundTripBool(v), v) + try XCTAssertEqual(jsRoundTripBool(v), v) } } - func testRoundTripString() { + func testRoundTripString() throws { for v in ["", "Hello, world!", "🧑‍🧑‍🧒"] { - XCTAssertEqual(jsRoundTripString(v), v) + try XCTAssertEqual(jsRoundTripString(v), v) } } - func testClass() { - let greeter = JsGreeter("Alice", "Hello") - XCTAssertEqual(greeter.greet(), "Hello, Alice!") - greeter.changeName("Bob") - XCTAssertEqual(greeter.greet(), "Hello, Bob!") + func testClass() throws { + let greeter = try JsGreeter("Alice", "Hello") + XCTAssertEqual(try greeter.greet(), "Hello, Alice!") + try greeter.changeName("Bob") + XCTAssertEqual(try greeter.greet(), "Hello, Bob!") - greeter.name = "Charlie" - XCTAssertEqual(greeter.greet(), "Hello, Charlie!") - XCTAssertEqual(greeter.name, "Charlie") + try greeter.setName("Charlie") + XCTAssertEqual(try greeter.greet(), "Hello, Charlie!") + XCTAssertEqual(try greeter.name, "Charlie") - XCTAssertEqual(greeter.prefix, "Hello") + XCTAssertEqual(try greeter.prefix, "Hello") } } From 2e71e200276e3d1821c1cb523776e25b6ef976b9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 19:14:33 +0900 Subject: [PATCH 6/8] BridgeJS: Stablize the order of exported classs in the generated files --- .../Generated/BridgeJS.ExportSwift.swift | 100 +++++++++--------- .../JavaScript/BridgeJS.ExportSwift.json | 96 ++++++++--------- .../Sources/BridgeJSCore/ExportSwift.swift | 15 ++- .../Sources/BridgeJSTool/BridgeJSTool.swift | 2 +- 4 files changed, 109 insertions(+), 104 deletions(-) diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift index ce93f95b..b0656df9 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ExportSwift.swift @@ -6,6 +6,56 @@ @_spi(BridgeJS) import JavaScriptKit +@_expose(wasm, "bjs_PlayBridgeJS_init") +@_cdecl("bjs_PlayBridgeJS_init") +public func _bjs_PlayBridgeJS_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = PlayBridgeJS() + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PlayBridgeJS_update") +@_cdecl("bjs_PlayBridgeJS_update") +public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSourceBytes: Int32, swiftSourceLen: Int32, dtsSourceBytes: Int32, dtsSourceLen: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + do { + let swiftSource = String(unsafeUninitializedCapacity: Int(swiftSourceLen)) { b in + _swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped) + return Int(swiftSourceLen) + } + let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in + _swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped) + return Int(dtsSourceLen) + } + let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) + return Unmanaged.passRetained(ret).toOpaque() + } catch let error { + if let error = error.thrownValue.object { + withExtendedLifetime(error) { + _swift_js_throw(Int32(bitPattern: $0.id)) + } + } else { + let jsError = JSError(message: String(describing: error)) + withExtendedLifetime(jsError.jsObject) { + _swift_js_throw(Int32(bitPattern: $0.id)) + } + } + return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PlayBridgeJS_deinit") +@_cdecl("bjs_PlayBridgeJS_deinit") +public func _bjs_PlayBridgeJS_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + @_expose(wasm, "bjs_PlayBridgeJSOutput_outputJs") @_cdecl("bjs_PlayBridgeJSOutput_outputJs") public func _bjs_PlayBridgeJSOutput_outputJs(_self: UnsafeMutableRawPointer) -> Void { @@ -62,54 +112,4 @@ public func _bjs_PlayBridgeJSOutput_exportSwiftGlue(_self: UnsafeMutableRawPoint @_cdecl("bjs_PlayBridgeJSOutput_deinit") public func _bjs_PlayBridgeJSOutput_deinit(pointer: UnsafeMutableRawPointer) { Unmanaged.fromOpaque(pointer).release() -} - -@_expose(wasm, "bjs_PlayBridgeJS_init") -@_cdecl("bjs_PlayBridgeJS_init") -public func _bjs_PlayBridgeJS_init() -> UnsafeMutableRawPointer { - #if arch(wasm32) - let ret = PlayBridgeJS() - return Unmanaged.passRetained(ret).toOpaque() - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PlayBridgeJS_update") -@_cdecl("bjs_PlayBridgeJS_update") -public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSourceBytes: Int32, swiftSourceLen: Int32, dtsSourceBytes: Int32, dtsSourceLen: Int32) -> UnsafeMutableRawPointer { - #if arch(wasm32) - do { - let swiftSource = String(unsafeUninitializedCapacity: Int(swiftSourceLen)) { b in - _swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped) - return Int(swiftSourceLen) - } - let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in - _swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped) - return Int(dtsSourceLen) - } - let ret = try Unmanaged.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource) - return Unmanaged.passRetained(ret).toOpaque() - } catch let error { - if let error = error.thrownValue.object { - withExtendedLifetime(error) { - _swift_js_throw(Int32(bitPattern: $0.id)) - } - } else { - let jsError = JSError(message: String(describing: error)) - withExtendedLifetime(jsError.jsObject) { - _swift_js_throw(Int32(bitPattern: $0.id)) - } - } - return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped - } - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PlayBridgeJS_deinit") -@_cdecl("bjs_PlayBridgeJS_deinit") -public func _bjs_PlayBridgeJS_deinit(pointer: UnsafeMutableRawPointer) { - Unmanaged.fromOpaque(pointer).release() } \ No newline at end of file diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json index 910ee14a..c4d55d27 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -1,5 +1,53 @@ { "classes" : [ + { + "constructor" : { + "abiName" : "bjs_PlayBridgeJS_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_PlayBridgeJS_update", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "update", + "parameters" : [ + { + "label" : "swiftSource", + "name" : "swiftSource", + "type" : { + "string" : { + + } + } + }, + { + "label" : "dtsSource", + "name" : "dtsSource", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "PlayBridgeJSOutput" + } + } + } + ], + "name" : "PlayBridgeJS" + }, { "methods" : [ { @@ -68,54 +116,6 @@ } ], "name" : "PlayBridgeJSOutput" - }, - { - "constructor" : { - "abiName" : "bjs_PlayBridgeJS_init", - "effects" : { - "isAsync" : false, - "isThrows" : false - }, - "parameters" : [ - - ] - }, - "methods" : [ - { - "abiName" : "bjs_PlayBridgeJS_update", - "effects" : { - "isAsync" : false, - "isThrows" : true - }, - "name" : "update", - "parameters" : [ - { - "label" : "swiftSource", - "name" : "swiftSource", - "type" : { - "string" : { - - } - } - }, - { - "label" : "dtsSource", - "name" : "dtsSource", - "type" : { - "string" : { - - } - } - } - ], - "returnType" : { - "swiftHeapObject" : { - "_0" : "PlayBridgeJSOutput" - } - } - } - ], - "name" : "PlayBridgeJS" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 12960500..ac6faf18 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -56,7 +56,9 @@ class ExportSwift { fileprivate final class APICollector: SyntaxAnyVisitor { var exportedFunctions: [ExportedFunction] = [] - var exportedClasses: [String: ExportedClass] = [:] + /// The names of the exported classes, in the order they were written in the source file + var exportedClassNames: [String] = [] + var exportedClassByName: [String: ExportedClass] = [:] var errors: [DiagnosticError] = [] enum State { @@ -114,7 +116,7 @@ class ExportSwift { return .skipChildren case .classBody(let name): if let exportedFunction = visitFunction(node: node) { - exportedClasses[name]?.methods.append(exportedFunction) + exportedClassByName[name]?.methods.append(exportedFunction) } return .skipChildren } @@ -217,7 +219,7 @@ class ExportSwift { parameters: parameters, effects: effects ) - exportedClasses[name]?.constructor = constructor + exportedClassByName[name]?.constructor = constructor return .skipChildren } @@ -226,11 +228,12 @@ class ExportSwift { stateStack.push(state: .classBody(name: name)) guard node.attributes.hasJSAttribute() else { return .skipChildren } - exportedClasses[name] = ExportedClass( + exportedClassByName[name] = ExportedClass( name: name, constructor: nil, methods: [] ) + exportedClassNames.append(name) return .visitChildren } override func visitPost(_ node: ClassDeclSyntax) { @@ -242,7 +245,9 @@ class ExportSwift { let collector = APICollector(parent: self) collector.walk(sourceFile) exportedFunctions.append(contentsOf: collector.exportedFunctions) - exportedClasses.append(contentsOf: collector.exportedClasses.values) + exportedClasses.append(contentsOf: collector.exportedClassNames.map { + collector.exportedClassByName[$0]! + }) return collector.errors } diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift index 044bff0d..6096e2b3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift @@ -159,7 +159,7 @@ import SwiftParser ) let progress = ProgressReporting(verbose: doubleDashOptions["verbose"] == "true") let exporter = ExportSwift(progress: progress) - for inputFile in positionalArguments { + for inputFile in positionalArguments.sorted() { let sourceURL = URL(fileURLWithPath: inputFile) guard sourceURL.pathExtension == "swift" else { continue } let sourceContent = try String(contentsOf: sourceURL, encoding: .utf8) From 6222727b342e8de7884ada2e8d4ddc7bb6611847 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 19:29:46 +0900 Subject: [PATCH 7/8] ./Utilities/format.swift --- Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift | 8 +++++--- Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index ac6faf18..58d99752 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -245,9 +245,11 @@ class ExportSwift { let collector = APICollector(parent: self) collector.walk(sourceFile) exportedFunctions.append(contentsOf: collector.exportedFunctions) - exportedClasses.append(contentsOf: collector.exportedClassNames.map { - collector.exportedClassByName[$0]! - }) + exportedClasses.append( + contentsOf: collector.exportedClassNames.map { + collector.exportedClassByName[$0]! + } + ) return collector.errors } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index d9343399..1aaaa16e 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -480,7 +480,7 @@ struct ImportTS { leftParen: .leftParenToken(), type: IdentifierTypeSyntax(name: .identifier("JSException")), rightParen: .rightParenToken() - ) : nil, + ) : nil ) } static func buildAccessorEffect(throws: Bool, async: Bool) -> AccessorEffectSpecifiersSyntax { @@ -492,7 +492,7 @@ struct ImportTS { leftParen: .leftParenToken(), type: IdentifierTypeSyntax(name: .identifier("JSException")), rightParen: .rightParenToken() - ) : nil, + ) : nil ) } } From 818fa913c0e6be1575c583d4b429dfbf93ca503d Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 23 Jul 2025 23:05:20 +0900 Subject: [PATCH 8/8] BridgeJS: Wrap imported calls in try-catch to handle exceptions --- .../Sources/Generated/BridgeJS.ImportTS.swift | 9 ++ .../Generated/BridgeJS.ImportTS.swift | 6 ++ .../Sources/BridgeJSCore/ExportSwift.swift | 20 +--- .../Sources/BridgeJSCore/ImportTS.swift | 1 + .../Sources/BridgeJSLink/BridgeJSLink.swift | 38 ++++++-- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 17 ++++ .../ArrayParameter.Import.js | 22 ++++- .../BridgeJSLinkTests/Interface.Import.js | 31 ++++-- .../PrimitiveParameters.Export.js | 4 + .../PrimitiveParameters.Import.js | 10 +- .../PrimitiveReturn.Export.js | 4 + .../PrimitiveReturn.Import.js | 22 ++++- .../StringParameter.Export.js | 4 + .../StringParameter.Import.js | 24 +++-- .../BridgeJSLinkTests/StringReturn.Export.js | 4 + .../BridgeJSLinkTests/StringReturn.Import.js | 14 ++- .../BridgeJSLinkTests/SwiftClass.Export.js | 4 + .../BridgeJSLinkTests/Throws.Export.js | 4 + .../BridgeJSLinkTests/TypeAlias.Import.js | 10 +- .../TypeScriptClass.Import.js | 66 +++++++++---- .../VoidParameterVoidReturn.Export.js | 4 + .../VoidParameterVoidReturn.Import.js | 10 +- .../ImportTSTests/ArrayParameter.swift | 9 ++ .../ImportTSTests/Interface.swift | 9 ++ .../ImportTSTests/PrimitiveParameters.swift | 3 + .../ImportTSTests/PrimitiveReturn.swift | 6 ++ .../ImportTSTests/StringParameter.swift | 6 ++ .../ImportTSTests/StringReturn.swift | 3 + .../ImportTSTests/TypeAlias.swift | 3 + .../ImportTSTests/TypeScriptClass.swift | 18 ++++ .../VoidParameterVoidReturn.swift | 3 + Plugins/PackageToJS/Templates/runtime.mjs | 8 +- Runtime/src/object-heap.ts | 8 +- .../JavaScriptKit/BridgeJSInstrincics.swift | 17 +++- .../FundamentalObjects/JSObject.swift | 2 +- Sources/JavaScriptKit/JSException.swift | 1 + Sources/_CJavaScriptKit/_CJavaScriptKit.c | 5 + .../include/BridgeJSInstrincics.h | 11 +++ .../include/WasmGlobalMacros.h | 45 +++++++++ .../_CJavaScriptKit/include/module.modulemap | 1 + .../Generated/BridgeJS.ImportTS.swift | 96 +++++++++++++++++++ .../JavaScript/BridgeJS.ImportTS.json | 72 ++++++++++++++ .../BridgeJSRuntimeTests/ImportAPITests.swift | 37 +++++++ Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 6 +- Tests/prelude.mjs | 23 +++++ 45 files changed, 640 insertions(+), 80 deletions(-) create mode 100644 Sources/_CJavaScriptKit/include/BridgeJSInstrincics.h create mode 100644 Sources/_CJavaScriptKit/include/WasmGlobalMacros.h diff --git a/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift b/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift index e8aa53b4..871938cf 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift @@ -16,6 +16,9 @@ func benchmarkHelperNoop() throws(JSException) -> Void { } #endif bjs_benchmarkHelperNoop() + if let error = _swift_js_take_exception() { + throw error + } } func benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void { @@ -28,6 +31,9 @@ func benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void { } #endif bjs_benchmarkHelperNoopWithNumber(n) + if let error = _swift_js_take_exception() { + throw error + } } func benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Void { @@ -44,4 +50,7 @@ func benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Vo _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_benchmarkRunner(nameId, Int32(bitPattern: body.id)) + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift index 4f7c4865..5d82db08 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.ImportTS.swift @@ -16,6 +16,9 @@ func createTS2Skeleton() throws(JSException) -> TS2Skeleton { } #endif let ret = bjs_createTS2Skeleton() + if let error = _swift_js_take_exception() { + throw error + } return TS2Skeleton(takingThis: ret) } @@ -44,6 +47,9 @@ struct TS2Skeleton { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } let ret = bjs_TS2Skeleton_convert(Int32(bitPattern: self.this.id), tsId) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 58d99752..fefcf40c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -433,25 +433,7 @@ class ExportSwift { } func lowerReturnValue(returnType: BridgeType) { - switch returnType { - case .void: - abiReturnType = nil - case .bool: - abiReturnType = .i32 - case .int: - abiReturnType = .i32 - case .float: - abiReturnType = .f32 - case .double: - abiReturnType = .f64 - case .string: - abiReturnType = nil - case .jsObject: - abiReturnType = .i32 - case .swiftHeapObject: - // UnsafeMutableRawPointer is returned as an i32 pointer - abiReturnType = .pointer - } + abiReturnType = returnType.abiReturnType switch returnType { case .void: break diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 1aaaa16e..37181114 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -151,6 +151,7 @@ struct ImportTS { } else { body.append("let ret = \(raw: call)") } + body.append("if let error = _swift_js_take_exception() { throw error }") } func liftReturnValue(returnType: BridgeType) throws { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 05a29aab..4d9ba596 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -118,6 +118,7 @@ struct BridgeJSLink { export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -161,6 +162,9 @@ struct BridgeJSLink { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { @@ -419,16 +423,24 @@ struct BridgeJSLink { func renderFunction( name: String, - returnExpr: String? + returnExpr: String?, + returnType: BridgeType ) -> [String] { var funcLines: [String] = [] funcLines.append( "function \(name)(\(parameterNames.joined(separator: ", "))) {" ) - funcLines.append(contentsOf: bodyLines.map { $0.indent(count: 4) }) + funcLines.append("try {".indent(count: 4)) + funcLines.append(contentsOf: bodyLines.map { $0.indent(count: 8) }) if let returnExpr = returnExpr { - funcLines.append("return \(returnExpr);".indent(count: 4)) + funcLines.append("return \(returnExpr);".indent(count: 8)) } + funcLines.append("} catch (error) {".indent(count: 4)) + funcLines.append("setException(error);".indent(count: 8)) + if let abiReturnType = returnType.abiReturnType { + funcLines.append("return \(abiReturnType.placeholderValue)".indent(count: 8)) + } + funcLines.append("}".indent(count: 4)) funcLines.append("}") return funcLines } @@ -518,7 +530,8 @@ struct BridgeJSLink { let returnExpr = try thunkBuilder.lowerReturnValue(returnType: function.returnType) let funcLines = thunkBuilder.renderFunction( name: function.abiName(context: nil), - returnExpr: returnExpr + returnExpr: returnExpr, + returnType: function.returnType ) importObjectBuilder.appendDts( [ @@ -591,7 +604,8 @@ struct BridgeJSLink { let abiName = constructor.abiName(context: type) let funcLines = thunkBuilder.renderFunction( name: abiName, - returnExpr: returnExpr + returnExpr: returnExpr, + returnType: returnType ) importObjectBuilder.assignToImportObject(name: abiName, function: funcLines) importObjectBuilder.appendDts([ @@ -611,7 +625,8 @@ struct BridgeJSLink { let returnExpr = try emitCall(thunkBuilder) let funcLines = thunkBuilder.renderFunction( name: abiName, - returnExpr: returnExpr + returnExpr: returnExpr, + returnType: property.type ) return (funcLines, []) } @@ -629,7 +644,8 @@ struct BridgeJSLink { let returnExpr = try thunkBuilder.lowerReturnValue(returnType: method.returnType) let funcLines = thunkBuilder.renderFunction( name: method.abiName(context: context), - returnExpr: returnExpr + returnExpr: returnExpr, + returnType: method.returnType ) return (funcLines, []) } @@ -667,3 +683,11 @@ extension BridgeType { } } } + +extension WasmCoreType { + fileprivate var placeholderValue: String { + switch self { + case .i32, .i64, .f32, .f64, .pointer: return "0" + } + } +} diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 873849f9..5bfcc414 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -101,3 +101,20 @@ struct ImportedModuleSkeleton: Codable { let moduleName: String var children: [ImportedFileSkeleton] } + +extension BridgeType { + var abiReturnType: WasmCoreType? { + switch self { + case .void: return nil + case .bool: return .i32 + case .int: return .i32 + case .float: return .f32 + case .double: return .f64 + case .string: return nil + case .jsObject: return .i32 + case .swiftHeapObject: + // UnsafeMutableRawPointer is returned as an i32 pointer + return .pointer + } + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js index f619fcf2..c90b3919 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,18 +48,33 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_checkArray"] = function bjs_checkArray(a) { - options.imports.checkArray(swift.memory.getObject(a)); + try { + options.imports.checkArray(swift.memory.getObject(a)); + } catch (error) { + setException(error); + } } TestModule["bjs_checkArrayWithLength"] = function bjs_checkArrayWithLength(a, b) { - options.imports.checkArrayWithLength(swift.memory.getObject(a), b); + try { + options.imports.checkArrayWithLength(swift.memory.getObject(a), b); + } catch (error) { + setException(error); + } } TestModule["bjs_checkArray"] = function bjs_checkArray(a) { - options.imports.checkArray(swift.memory.getObject(a)); + try { + options.imports.checkArray(swift.memory.getObject(a)); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js index fc6c3fcc..4d88bcdb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,21 +48,39 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_returnAnimatable"] = function bjs_returnAnimatable() { - let ret = options.imports.returnAnimatable(); - return swift.memory.retain(ret); + try { + let ret = options.imports.returnAnimatable(); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } } TestModule["bjs_Animatable_animate"] = function bjs_Animatable_animate(self, keyframes, options) { - let ret = swift.memory.getObject(self).animate(swift.memory.getObject(keyframes), swift.memory.getObject(options)); - return swift.memory.retain(ret); + try { + let ret = swift.memory.getObject(self).animate(swift.memory.getObject(keyframes), swift.memory.getObject(options)); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } } TestModule["bjs_Animatable_getAnimations"] = function bjs_Animatable_getAnimations(self, options) { - let ret = swift.memory.getObject(self).getAnimations(swift.memory.getObject(options)); - return swift.memory.retain(ret); + try { + let ret = swift.memory.getObject(self).getAnimations(swift.memory.getObject(options)); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js index 0d31689c..1f4d6cbc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js index c8a58a3d..c6413b6b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,12 +48,19 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_check"] = function bjs_check(a, b) { - options.imports.check(a, b); + try { + options.imports.check(a, b); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js index b2a16684..01dbcb74 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js index 690514e3..2a3292bc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,17 +48,30 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_checkNumber"] = function bjs_checkNumber() { - let ret = options.imports.checkNumber(); - return ret; + try { + let ret = options.imports.checkNumber(); + return ret; + } catch (error) { + setException(error); + return 0 + } } TestModule["bjs_checkBoolean"] = function bjs_checkBoolean() { - let ret = options.imports.checkBoolean(); - return ret !== 0; + try { + let ret = options.imports.checkBoolean(); + return ret !== 0; + } catch (error) { + setException(error); + return 0 + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js index ca37f7f5..7c2e883d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js index 466b7786..c12e6c1e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,19 +48,30 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_checkString"] = function bjs_checkString(a) { - const aObject = swift.memory.getObject(a); - swift.memory.release(a); - options.imports.checkString(aObject); + try { + const aObject = swift.memory.getObject(a); + swift.memory.release(a); + options.imports.checkString(aObject); + } catch (error) { + setException(error); + } } TestModule["bjs_checkStringWithLength"] = function bjs_checkStringWithLength(a, b) { - const aObject = swift.memory.getObject(a); - swift.memory.release(a); - options.imports.checkStringWithLength(aObject, b); + try { + const aObject = swift.memory.getObject(a); + swift.memory.release(a); + options.imports.checkStringWithLength(aObject, b); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js index d752e9e7..0362f3c4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js index fb2ee18e..bb78c255 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,14 +48,21 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_checkString"] = function bjs_checkString() { - let ret = options.imports.checkString(); - tmpRetBytes = textEncoder.encode(ret); - return tmpRetBytes.length; + try { + let ret = options.imports.checkString(); + tmpRetBytes = textEncoder.encode(ret); + return tmpRetBytes.length; + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js index 58df1f25..7a5938a1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js index 6ebd05df..d0f9b623 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js index 41af7fa1..30639b4a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,12 +48,19 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_checkSimple"] = function bjs_checkSimple(a) { - options.imports.checkSimple(a); + try { + options.imports.checkSimple(a); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js index 52d36b77..f5cdc9ef 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,39 +48,68 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_Greeter_init"] = function bjs_Greeter_init(name) { - const nameObject = swift.memory.getObject(name); - swift.memory.release(name); - let ret = new options.imports.Greeter(nameObject); - return swift.memory.retain(ret); + try { + const nameObject = swift.memory.getObject(name); + swift.memory.release(name); + let ret = new options.imports.Greeter(nameObject); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } } TestModule["bjs_Greeter_name_get"] = function bjs_Greeter_name_get(self) { - let ret = swift.memory.getObject(self).name; - tmpRetBytes = textEncoder.encode(ret); - return tmpRetBytes.length; + try { + let ret = swift.memory.getObject(self).name; + tmpRetBytes = textEncoder.encode(ret); + return tmpRetBytes.length; + } catch (error) { + setException(error); + } } TestModule["bjs_Greeter_name_set"] = function bjs_Greeter_name_set(self, newValue) { - const newValueObject = swift.memory.getObject(newValue); - swift.memory.release(newValue); - swift.memory.getObject(self).name = newValueObject; + try { + const newValueObject = swift.memory.getObject(newValue); + swift.memory.release(newValue); + swift.memory.getObject(self).name = newValueObject; + } catch (error) { + setException(error); + } } TestModule["bjs_Greeter_age_get"] = function bjs_Greeter_age_get(self) { - let ret = swift.memory.getObject(self).age; - return ret; + try { + let ret = swift.memory.getObject(self).age; + return ret; + } catch (error) { + setException(error); + return 0 + } } TestModule["bjs_Greeter_greet"] = function bjs_Greeter_greet(self) { - let ret = swift.memory.getObject(self).greet(); - tmpRetBytes = textEncoder.encode(ret); - return tmpRetBytes.length; + try { + let ret = swift.memory.getObject(self).greet(); + tmpRetBytes = textEncoder.encode(ret); + return tmpRetBytes.length; + } catch (error) { + setException(error); + } } TestModule["bjs_Greeter_changeName"] = function bjs_Greeter_changeName(self, name) { - const nameObject = swift.memory.getObject(name); - swift.memory.release(name); - swift.memory.getObject(self).changeName(nameObject); + try { + const nameObject = swift.memory.getObject(name); + swift.memory.release(name); + swift.memory.getObject(self).changeName(nameObject); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js index 29019e90..c7086eda 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -50,6 +51,9 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js index f57197b0..2482082c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js @@ -7,6 +7,7 @@ export async function createInstantiator(options, swift) { let instance; let memory; + let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); @@ -47,12 +48,19 @@ export async function createInstantiator(options, swift) { } const TestModule = importObject["TestModule"] = {}; TestModule["bjs_check"] = function bjs_check() { - options.imports.check(); + try { + options.imports.check(); + } catch (error) { + setException(error); + } } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } }, /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift index 9faf6209..34841ae8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift @@ -16,6 +16,9 @@ func checkArray(_ a: JSObject) throws(JSException) -> Void { } #endif bjs_checkArray(Int32(bitPattern: a.id)) + if let error = _swift_js_take_exception() { + throw error + } } func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void { @@ -28,6 +31,9 @@ func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Voi } #endif bjs_checkArrayWithLength(Int32(bitPattern: a.id), b) + if let error = _swift_js_take_exception() { + throw error + } } func checkArray(_ a: JSObject) throws(JSException) -> Void { @@ -40,4 +46,7 @@ func checkArray(_ a: JSObject) throws(JSException) -> Void { } #endif bjs_checkArray(Int32(bitPattern: a.id)) + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift index 8968a9f3..be9f524e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift @@ -16,6 +16,9 @@ func returnAnimatable() throws(JSException) -> Animatable { } #endif let ret = bjs_returnAnimatable() + if let error = _swift_js_take_exception() { + throw error + } return Animatable(takingThis: ret) } @@ -40,6 +43,9 @@ struct Animatable { } #endif let ret = bjs_Animatable_animate(Int32(bitPattern: self.this.id), Int32(bitPattern: keyframes.id), Int32(bitPattern: options.id)) + if let error = _swift_js_take_exception() { + throw error + } return JSObject(id: UInt32(bitPattern: ret)) } @@ -53,6 +59,9 @@ struct Animatable { } #endif let ret = bjs_Animatable_getAnimations(Int32(bitPattern: self.this.id), Int32(bitPattern: options.id)) + if let error = _swift_js_take_exception() { + throw error + } return JSObject(id: UInt32(bitPattern: ret)) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift index afef0de3..c47f5f40 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift @@ -16,4 +16,7 @@ func check(_ a: Double, _ b: Bool) throws(JSException) -> Void { } #endif bjs_check(a, Int32(b ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift index 11bd7d4f..cf26a52f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift @@ -16,6 +16,9 @@ func checkNumber() throws(JSException) -> Double { } #endif let ret = bjs_checkNumber() + if let error = _swift_js_take_exception() { + throw error + } return Double(ret) } @@ -29,5 +32,8 @@ func checkBoolean() throws(JSException) -> Bool { } #endif let ret = bjs_checkBoolean() + if let error = _swift_js_take_exception() { + throw error + } return ret == 1 } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift index fb783de1..aabffacc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift @@ -20,6 +20,9 @@ func checkString(_ a: String) throws(JSException) -> Void { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_checkString(aId) + if let error = _swift_js_take_exception() { + throw error + } } func checkStringWithLength(_ a: String, _ b: Double) throws(JSException) -> Void { @@ -36,4 +39,7 @@ func checkStringWithLength(_ a: String, _ b: Double) throws(JSException) -> Void _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_checkStringWithLength(aId, b) + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift index fd26de22..af7b5162 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift @@ -16,6 +16,9 @@ func checkString() throws(JSException) -> String { } #endif let ret = bjs_checkString() + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift index a8523110..7523101f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift @@ -16,4 +16,7 @@ func checkSimple(_ a: Double) throws(JSException) -> Void { } #endif bjs_checkSimple(a) + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift index d20b2a9c..7a1f2a2c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift @@ -31,6 +31,9 @@ struct Greeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } let ret = bjs_Greeter_init(nameId) + if let error = _swift_js_take_exception() { + throw error + } self.this = JSObject(id: UInt32(bitPattern: ret)) } @@ -45,6 +48,9 @@ struct Greeter { } #endif let ret = bjs_Greeter_name_get(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -66,6 +72,9 @@ struct Greeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_Greeter_name_set(Int32(bitPattern: self.this.id), newValueId) + if let error = _swift_js_take_exception() { + throw error + } } var age: Double { @@ -79,6 +88,9 @@ struct Greeter { } #endif let ret = bjs_Greeter_age_get(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return Double(ret) } } @@ -93,6 +105,9 @@ struct Greeter { } #endif let ret = bjs_Greeter_greet(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -113,6 +128,9 @@ struct Greeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_Greeter_changeName(Int32(bitPattern: self.this.id), nameId) + if let error = _swift_js_take_exception() { + throw error + } } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift index 7c2b3e14..ae7ae0e8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift @@ -16,4 +16,7 @@ func check() throws(JSException) -> Void { } #endif bjs_check() + if let error = _swift_js_take_exception() { + throw error + } } \ No newline at end of file diff --git a/Plugins/PackageToJS/Templates/runtime.mjs b/Plugins/PackageToJS/Templates/runtime.mjs index 66a2e0ad..447f2090 100644 --- a/Plugins/PackageToJS/Templates/runtime.mjs +++ b/Plugins/PackageToJS/Templates/runtime.mjs @@ -242,11 +242,11 @@ else if (typeof self !== "undefined") { class JSObjectSpace { constructor() { this._heapValueById = new Map(); - this._heapValueById.set(0, globalVariable); + this._heapValueById.set(1, globalVariable); this._heapEntryByValue = new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); - // Note: 0 is preserved for global - this._heapNextKey = 1; + this._heapEntryByValue.set(globalVariable, { id: 1, rc: 1 }); + // Note: 0 is preserved for invalid references, 1 is preserved for globalThis + this._heapNextKey = 2; } retain(value) { const entry = this._heapEntryByValue.get(value); diff --git a/Runtime/src/object-heap.ts b/Runtime/src/object-heap.ts index ecc2d218..b90fc4ef 100644 --- a/Runtime/src/object-heap.ts +++ b/Runtime/src/object-heap.ts @@ -12,13 +12,13 @@ export class JSObjectSpace { constructor() { this._heapValueById = new Map(); - this._heapValueById.set(0, globalVariable); + this._heapValueById.set(1, globalVariable); this._heapEntryByValue = new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); + this._heapEntryByValue.set(globalVariable, { id: 1, rc: 1 }); - // Note: 0 is preserved for global - this._heapNextKey = 1; + // Note: 0 is preserved for invalid references, 1 is preserved for globalThis + this._heapNextKey = 2; } retain(value: any) { diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index e2c54026..1a8bad7f 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -1,5 +1,7 @@ +import _CJavaScriptKit + #if !arch(wasm32) -private func _onlyAvailableOnWasm() -> Never { +@usableFromInline func _onlyAvailableOnWasm() -> Never { fatalError("Only available on WebAssembly") } #endif @@ -57,3 +59,16 @@ private func _onlyAvailableOnWasm() -> Never { _onlyAvailableOnWasm() } #endif + +@_spi(BridgeJS) @_transparent public func _swift_js_take_exception() -> JSException? { + #if arch(wasm32) + let value = _swift_js_exception_get() + if _fastPath(value == 0) { + return nil + } + _swift_js_exception_set(0) + return JSException(JSObject(id: UInt32(bitPattern: value)).jsValue) + #else + _onlyAvailableOnWasm() + #endif +} diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift index 8892e987..b9330ae8 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift @@ -218,7 +218,7 @@ public class JSObject: Equatable, ExpressibleByDictionaryLiteral { return swjs_instanceof(id, constructor.id) } - static let _JS_Predef_Value_Global: JavaScriptObjectRef = 0 + static let _JS_Predef_Value_Global: JavaScriptObjectRef = 1 /// A `JSObject` of the global scope object. /// This allows access to the global properties and global names by accessing the `JSObject` returned. diff --git a/Sources/JavaScriptKit/JSException.swift b/Sources/JavaScriptKit/JSException.swift index 714b83aa..4d95e207 100644 --- a/Sources/JavaScriptKit/JSException.swift +++ b/Sources/JavaScriptKit/JSException.swift @@ -34,6 +34,7 @@ public struct JSException: Error, Equatable, CustomStringConvertible { /// Initializes a new JSException instance with a value thrown from JavaScript. /// /// Only available within the package. This must be called on the thread where the exception object created. + @usableFromInline package init(_ thrownValue: JSValue) { self._thrownValue = thrownValue // Capture the stringified representation on the object owner thread diff --git a/Sources/_CJavaScriptKit/_CJavaScriptKit.c b/Sources/_CJavaScriptKit/_CJavaScriptKit.c index a3288180..9df18b88 100644 --- a/Sources/_CJavaScriptKit/_CJavaScriptKit.c +++ b/Sources/_CJavaScriptKit/_CJavaScriptKit.c @@ -1,4 +1,6 @@ +#include #include "_CJavaScriptKit.h" +#include "WasmGlobalMacros.h" #if __wasm32__ # ifndef __wasi__ # if __has_include("malloc.h") @@ -60,6 +62,9 @@ int swjs_library_features(void) { return _library_features(); } # endif + +WASM_GLOBAL_DEFINE_STORAGE(_swift_js_exception, i32) +WASM_EXPORT_NAME(_swift_js_exception, _swift_js_exception) #endif int swjs_get_worker_thread_id_cached(void) { diff --git a/Sources/_CJavaScriptKit/include/BridgeJSInstrincics.h b/Sources/_CJavaScriptKit/include/BridgeJSInstrincics.h new file mode 100644 index 00000000..4f5ba93c --- /dev/null +++ b/Sources/_CJavaScriptKit/include/BridgeJSInstrincics.h @@ -0,0 +1,11 @@ +#ifndef _CJavaScriptKit_BridgeJSInstrincics_h +#define _CJavaScriptKit_BridgeJSInstrincics_h + +#include +#include "WasmGlobalMacros.h" + +#if __wasm__ +WASM_GLOBAL_DEFINE_INLINE_ACCESSORS(_swift_js_exception, i32, int32_t) +#endif + +#endif diff --git a/Sources/_CJavaScriptKit/include/WasmGlobalMacros.h b/Sources/_CJavaScriptKit/include/WasmGlobalMacros.h new file mode 100644 index 00000000..5e7aeb97 --- /dev/null +++ b/Sources/_CJavaScriptKit/include/WasmGlobalMacros.h @@ -0,0 +1,45 @@ +#ifndef _CJavaScriptKit_WasmGlobalMacros_h +#define _CJavaScriptKit_WasmGlobalMacros_h + +// Define an inline getter for a Wasm global +#define WASM_GLOBAL_DEFINE_INLINE_GETTER(name, core_type, c_type) \ + static inline c_type name##_get(void) { \ + c_type val; \ + __asm__( \ + ".globaltype " #name ", " #core_type "\n" \ + "global.get " #name "\n" \ + "local.set %0\n" \ + : "=r"(val)); \ + return val; \ + } + +// Define an inline setter for a Wasm global +#define WASM_GLOBAL_DEFINE_INLINE_SETTER(name, core_type, c_type) \ + static inline void name##_set(c_type val) { \ + __asm__( \ + ".globaltype " #name ", " #core_type "\n" \ + "local.get %0\n" \ + "global.set " #name "\n" \ + : : "r"(val)); \ + } + +// Define an inline getter and setter for a Wasm global +#define WASM_GLOBAL_DEFINE_INLINE_ACCESSORS(name, core_type, c_type) \ + WASM_GLOBAL_DEFINE_INLINE_GETTER(name, core_type, c_type) \ + WASM_GLOBAL_DEFINE_INLINE_SETTER(name, core_type, c_type) + +// Define a Wasm global storage +#define WASM_GLOBAL_DEFINE_STORAGE(name, core_type) \ + __asm__( \ + ".globaltype " #name ", " #core_type "\n" \ + ".global " #name "\n" \ + #name ":\n" \ + ); + +// Define an export name for a Wasm value +#define WASM_EXPORT_NAME(name, export_name) \ + __asm__( \ + ".export_name " #name ", " #export_name "\n" \ + ); + +#endif diff --git a/Sources/_CJavaScriptKit/include/module.modulemap b/Sources/_CJavaScriptKit/include/module.modulemap index 8f4b6926..3afe1283 100644 --- a/Sources/_CJavaScriptKit/include/module.modulemap +++ b/Sources/_CJavaScriptKit/include/module.modulemap @@ -1,4 +1,5 @@ module _CJavaScriptKit { header "_CJavaScriptKit.h" + header "BridgeJSInstrincics.h" export * } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift index 0122fe7e..fd558ab8 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ImportTS.swift @@ -16,6 +16,9 @@ func jsRoundTripVoid() throws(JSException) -> Void { } #endif bjs_jsRoundTripVoid() + if let error = _swift_js_take_exception() { + throw error + } } func jsRoundTripNumber(_ v: Double) throws(JSException) -> Double { @@ -28,6 +31,9 @@ func jsRoundTripNumber(_ v: Double) throws(JSException) -> Double { } #endif let ret = bjs_jsRoundTripNumber(v) + if let error = _swift_js_take_exception() { + throw error + } return Double(ret) } @@ -41,6 +47,9 @@ func jsRoundTripBool(_ v: Bool) throws(JSException) -> Bool { } #endif let ret = bjs_jsRoundTripBool(Int32(v ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } return ret == 1 } @@ -58,6 +67,75 @@ func jsRoundTripString(_ v: String) throws(JSException) -> String { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } let ret = bjs_jsRoundTripString(vId) + if let error = _swift_js_take_exception() { + throw error + } + return String(unsafeUninitializedCapacity: Int(ret)) { b in + _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) + return Int(ret) + } +} + +func jsThrowOrVoid(_ shouldThrow: Bool) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsThrowOrVoid") + func bjs_jsThrowOrVoid(_ shouldThrow: Int32) -> Void + #else + func bjs_jsThrowOrVoid(_ shouldThrow: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_jsThrowOrVoid(Int32(shouldThrow ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } +} + +func jsThrowOrNumber(_ shouldThrow: Bool) throws(JSException) -> Double { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsThrowOrNumber") + func bjs_jsThrowOrNumber(_ shouldThrow: Int32) -> Float64 + #else + func bjs_jsThrowOrNumber(_ shouldThrow: Int32) -> Float64 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_jsThrowOrNumber(Int32(shouldThrow ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } + return Double(ret) +} + +func jsThrowOrBool(_ shouldThrow: Bool) throws(JSException) -> Bool { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsThrowOrBool") + func bjs_jsThrowOrBool(_ shouldThrow: Int32) -> Int32 + #else + func bjs_jsThrowOrBool(_ shouldThrow: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_jsThrowOrBool(Int32(shouldThrow ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } + return ret == 1 +} + +func jsThrowOrString(_ shouldThrow: Bool) throws(JSException) -> String { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsThrowOrString") + func bjs_jsThrowOrString(_ shouldThrow: Int32) -> Int32 + #else + func bjs_jsThrowOrString(_ shouldThrow: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_jsThrowOrString(Int32(shouldThrow ? 1 : 0)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -93,6 +171,9 @@ struct JsGreeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } let ret = bjs_JsGreeter_init(nameId, prefixId) + if let error = _swift_js_take_exception() { + throw error + } self.this = JSObject(id: UInt32(bitPattern: ret)) } @@ -107,6 +188,9 @@ struct JsGreeter { } #endif let ret = bjs_JsGreeter_name_get(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -128,6 +212,9 @@ struct JsGreeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_JsGreeter_name_set(Int32(bitPattern: self.this.id), newValueId) + if let error = _swift_js_take_exception() { + throw error + } } var prefix: String { @@ -141,6 +228,9 @@ struct JsGreeter { } #endif let ret = bjs_JsGreeter_prefix_get(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -158,6 +248,9 @@ struct JsGreeter { } #endif let ret = bjs_JsGreeter_greet(Int32(bitPattern: self.this.id)) + if let error = _swift_js_take_exception() { + throw error + } return String(unsafeUninitializedCapacity: Int(ret)) { b in _swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) return Int(ret) @@ -178,6 +271,9 @@ struct JsGreeter { _swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) } bjs_JsGreeter_changeName(Int32(bitPattern: self.this.id), nameId) + if let error = _swift_js_take_exception() { + throw error + } } } \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ImportTS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ImportTS.json index ad8fcd87..bf3190b8 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ImportTS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ImportTS.json @@ -64,6 +64,78 @@ "returnType" : { "string" : { + } + } + }, + { + "name" : "jsThrowOrVoid", + "parameters" : [ + { + "name" : "shouldThrow", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "name" : "jsThrowOrNumber", + "parameters" : [ + { + "name" : "shouldThrow", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "name" : "jsThrowOrBool", + "parameters" : [ + { + "name" : "shouldThrow", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "name" : "jsThrowOrString", + "parameters" : [ + { + "name" : "shouldThrow", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "string" : { + } } } diff --git a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift index ad7b64f4..f0112ed1 100644 --- a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift @@ -35,6 +35,43 @@ class ImportAPITests: XCTestCase { } } + func ensureThrows(_ f: (Bool) throws(JSException) -> T) throws { + do { + _ = try f(true) + XCTFail("Expected exception") + } catch { + XCTAssertTrue(error.description.contains("TestError")) + } + } + func ensureDoesNotThrow(_ f: (Bool) throws(JSException) -> T, _ assertValue: (T) -> Void) throws { + do { + let result = try f(false) + assertValue(result) + } catch { + XCTFail("Expected no exception") + } + } + + func testThrowOrVoid() throws { + try ensureThrows(jsThrowOrVoid) + try ensureDoesNotThrow(jsThrowOrVoid, { _ in }) + } + + func testThrowOrNumber() throws { + try ensureThrows(jsThrowOrNumber) + try ensureDoesNotThrow(jsThrowOrNumber, { XCTAssertEqual($0, 1) }) + } + + func testThrowOrBool() throws { + try ensureThrows(jsThrowOrBool) + try ensureDoesNotThrow(jsThrowOrBool, { XCTAssertEqual($0, true) }) + } + + func testThrowOrString() throws { + try ensureThrows(jsThrowOrString) + try ensureDoesNotThrow(jsThrowOrString, { XCTAssertEqual($0, "Hello, world!") }) + } + func testClass() throws { let greeter = try JsGreeter("Alice", "Hello") XCTAssertEqual(try greeter.greet(), "Hello, Alice!") diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 664dd447..b03ef570 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -2,6 +2,10 @@ export function jsRoundTripVoid(): void export function jsRoundTripNumber(v: number): number export function jsRoundTripBool(v: boolean): boolean export function jsRoundTripString(v: string): string +export function jsThrowOrVoid(shouldThrow: boolean): void +export function jsThrowOrNumber(shouldThrow: boolean): number +export function jsThrowOrBool(shouldThrow: boolean): boolean +export function jsThrowOrString(shouldThrow: boolean): string export class JsGreeter { name: string; @@ -9,4 +13,4 @@ export class JsGreeter { constructor(name: string, prefix: string); greet(): string; changeName(name: string): void; -} \ No newline at end of file +} diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 0d49045b..c6ac428a 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -19,6 +19,29 @@ export async function setupOptions(options, context) { "jsRoundTripString": (v) => { return v; }, + "jsThrowOrVoid": (shouldThrow) => { + if (shouldThrow) { + throw new Error("TestError"); + } + }, + "jsThrowOrNumber": (shouldThrow) => { + if (shouldThrow) { + throw new Error("TestError"); + } + return 1; + }, + "jsThrowOrBool": (shouldThrow) => { + if (shouldThrow) { + throw new Error("TestError"); + } + return true; + }, + "jsThrowOrString": (shouldThrow) => { + if (shouldThrow) { + throw new Error("TestError"); + } + return "Hello, world!"; + }, JsGreeter: class { /** * @param {string} name