From ab19ba993e9dc48d49ca8b1e9d19ec428998cb5b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 23 Mar 2026 10:57:21 +0000 Subject: [PATCH 01/10] BridgeJS: support imports of `Promise` JS as `async` Swift --- .../Generated/JavaScript/BridgeJS.json | 15 + .../Generated/JavaScript/BridgeJS.json | 10 + .../Sources/BridgeJSCore/ImportTS.swift | 80 ++- .../BridgeJSCore/SwiftToSkeleton.swift | 12 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 17 +- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 3 + .../TS2Swift/JavaScript/src/processor.js | 33 +- .../test/__snapshots__/ts2swift.test.js.snap | 14 +- .../Inputs/MacroSwift/AsyncImport.swift | 6 + .../BridgeJSCodegenTests/ArrayTypes.json | 35 ++ .../BridgeJSCodegenTests/AsyncImport.json | 151 +++++ .../BridgeJSCodegenTests/AsyncImport.swift | 138 ++++ .../CrossFileSkipsEmptySkeletons.json | 5 + .../BridgeJSCodegenTests/DictionaryTypes.json | 5 + .../BridgeJSCodegenTests/EnumRawType.json | 10 + .../FixedWidthIntegers.json | 40 ++ .../BridgeJSCodegenTests/GlobalGetter.json | 5 + .../GlobalThisImports.json | 15 + .../BridgeJSCodegenTests/ImportArray.json | 10 + .../InvalidPropertyNames.json | 25 + .../BridgeJSCodegenTests/JSClass.json | 25 + .../JSClassStaticFunctions.json | 30 + .../BridgeJSCodegenTests/JSValue.json | 10 + .../BridgeJSCodegenTests/Optionals.json | 40 ++ .../PrimitiveParameters.json | 5 + .../BridgeJSCodegenTests/PrimitiveReturn.json | 10 + .../BridgeJSCodegenTests/StringParameter.json | 10 + .../BridgeJSCodegenTests/StringReturn.json | 5 + .../BridgeJSCodegenTests/SwiftClass.json | 10 + .../SwiftClosureImports.json | 10 + .../SwiftStructImports.json | 5 + .../VoidParameterVoidReturn.json | 5 + .../BridgeJSLinkTests/AsyncImport.d.ts | 23 + .../BridgeJSLinkTests/AsyncImport.js | 268 ++++++++ .../AsyncImportTests.swift | 23 + .../BridgeJSRuntimeTests/ExportAPITests.swift | 2 +- .../Generated/BridgeJS.Macros.swift | 10 +- .../Generated/BridgeJS.swift | 98 ++- .../Generated/JavaScript/BridgeJS.json | 594 +++++++++++++++++- Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 6 + .../JSClosure+AsyncTests.swift | 6 +- Tests/prelude.mjs | 12 + 42 files changed, 1793 insertions(+), 43 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js create mode 100644 Tests/BridgeJSRuntimeTests/AsyncImportTests.swift diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json index 95c4ac18e..b2c33ac01 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json @@ -2839,6 +2839,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkHelperNoop", "parameters" : [ @@ -2850,6 +2855,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkHelperNoopWithNumber", "parameters" : [ { @@ -2868,6 +2878,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "benchmarkRunner", "parameters" : [ { diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json index 60eb694ff..1a21916ee 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json @@ -242,6 +242,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createTS2Swift", "parameters" : [ @@ -260,6 +265,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "convert", "parameters" : [ { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 1d1fe3aa9..47880b654 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -278,6 +278,40 @@ public struct ImportTS { } } + func liftAsyncReturnValue(originalReturnType: BridgeType) { + // For async imports, the extern function returns a Promise object ID (i32). + // We wrap it in JSPromise, await the resolved value, then lift to the target type. + abiReturnType = .i32 + body.write( + "let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret)))" + ) + if originalReturnType == .void { + body.write("_ = try await promise.value") + } else { + body.write("let resolved = try await promise.value") + let liftExpr: String + switch originalReturnType { + case .double: + liftExpr = "Double(resolved.number!)" + case .float: + liftExpr = "Float(resolved.number!)" + case .integer: + liftExpr = "Int(resolved.number!)" + case .string: + liftExpr = "resolved.string!" + case .bool: + liftExpr = "resolved.boolean!" + case .jsObject: + liftExpr = "resolved.object!" + case .jsValue: + liftExpr = "resolved" + default: + liftExpr = "resolved.object!" + } + body.write("return \(liftExpr)") + } + } + func assignThis(returnType: BridgeType) { guard case .jsObject = returnType else { preconditionFailure("assignThis can only be called with a jsObject return type") @@ -299,9 +333,13 @@ public struct ImportTS { return "\(raw: printer.lines.joined(separator: "\n"))" } - func renderThunkDecl(name: String, parameters: [Parameter], returnType: BridgeType) -> DeclSyntax { + func renderThunkDecl( + name: String, + parameters: [Parameter], + returnType: BridgeType, + effects: Effects = Effects(isAsync: false, isThrows: true) + ) -> DeclSyntax { let printer = CodeFragmentPrinter() - let effects = Effects(isAsync: false, isThrows: true) let signature = SwiftSignatureBuilder.buildFunctionSignature( parameters: parameters, returnType: returnType, @@ -359,22 +397,30 @@ public struct ImportTS { _ function: ImportedFunctionSkeleton, topLevelDecls: inout [DeclSyntax] ) throws -> [DeclSyntax] { + // For async functions, the ABI return type is always jsObject (the Promise). + // We tell CallJSEmission that the return type is jsObject so it captures the return value. + let abiReturnType: BridgeType = function.effects.isAsync ? .jsObject(nil) : function.returnType let builder = try CallJSEmission( moduleName: moduleName, abiName: function.abiName(context: nil), - returnType: function.returnType + returnType: abiReturnType ) for param in function.parameters { try builder.lowerParameter(param: param) } try builder.call() - try builder.liftReturnValue() + if function.effects.isAsync { + builder.liftAsyncReturnValue(originalReturnType: function.returnType) + } else { + try builder.liftReturnValue() + } topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( name: Self.thunkName(function: function), parameters: function.parameters, - returnType: function.returnType + returnType: function.returnType, + effects: function.effects ) .with(\.leadingTrivia, Self.renderDocumentation(documentation: function.documentation)) ] @@ -385,41 +431,53 @@ public struct ImportTS { var decls: [DeclSyntax] = [] func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { + let abiReturnType: BridgeType = method.effects.isAsync ? .jsObject(nil) : method.returnType let builder = try CallJSEmission( moduleName: moduleName, abiName: method.abiName(context: type), - returnType: method.returnType + returnType: abiReturnType ) try builder.lowerParameter(param: selfParameter) for param in method.parameters { try builder.lowerParameter(param: param) } try builder.call() - try builder.liftReturnValue() + if method.effects.isAsync { + builder.liftAsyncReturnValue(originalReturnType: method.returnType) + } else { + try builder.liftReturnValue() + } topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( name: Self.thunkName(type: type, method: method), parameters: [selfParameter] + method.parameters, - returnType: method.returnType + returnType: method.returnType, + effects: method.effects ) ] } func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { let abiName = method.abiName(context: type, operation: "static") - let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, returnType: method.returnType) + let abiReturnType: BridgeType = method.effects.isAsync ? .jsObject(nil) : method.returnType + let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, returnType: abiReturnType) for param in method.parameters { try builder.lowerParameter(param: param) } try builder.call() - try builder.liftReturnValue() + if method.effects.isAsync { + builder.liftAsyncReturnValue(originalReturnType: method.returnType) + } else { + try builder.liftReturnValue() + } topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( name: Self.thunkName(type: type, method: method), parameters: method.parameters, - returnType: method.returnType + returnType: method.returnType, + effects: method.effects ) ] } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index 81ad32813..e2ec07703 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -2067,7 +2067,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { let valueType: BridgeType } - /// Validates effects (throws required, async not supported) + /// Validates effects (throws required, async only supported for @JSFunction) private func validateEffects( _ effects: FunctionEffectSpecifiersSyntax?, node: some SyntaxProtocol, @@ -2083,7 +2083,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { ) return nil } - if effects.isAsync { + if effects.isAsync && attributeName != "JSFunction" { errors.append( DiagnosticError( node: node, @@ -2420,7 +2420,12 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { _ jsFunction: AttributeSyntax, _ node: FunctionDeclSyntax, ) -> ImportedFunctionSkeleton? { - guard validateEffects(node.signature.effectSpecifiers, node: node, attributeName: "JSFunction") != nil + guard + let effects = validateEffects( + node.signature.effectSpecifiers, + node: node, + attributeName: "JSFunction" + ) else { return nil } @@ -2446,6 +2451,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { from: from, parameters: parameters, returnType: returnType, + effects: effects, documentation: nil ) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index f69a4b266..e1f18183e 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -1233,7 +1233,7 @@ public struct BridgeJSLink { for method in type.methods { let methodName = method.jsName ?? method.name let methodSignature = - "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: Effects(isAsync: false, isThrows: false)));" + "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" printer.write(methodSignature) } @@ -3124,21 +3124,23 @@ extension BridgeJSLink { } let jsName = function.jsName ?? function.name let importRootExpr = function.from == .global ? "globalThis" : "imports" + // For async functions, the JS handler returns the Promise as a jsObject. + // The Swift side handles awaiting and lifting the resolved value. + let abiReturnType: BridgeType = function.effects.isAsync ? .jsObject(nil) : function.returnType let returnExpr = try thunkBuilder.call( name: jsName, fromObjectExpr: importRootExpr, - returnType: function.returnType + returnType: abiReturnType ) let funcLines = thunkBuilder.renderFunction( name: function.abiName(context: nil), returnExpr: returnExpr, - returnType: function.returnType + returnType: abiReturnType ) - let effects = Effects(isAsync: false, isThrows: false) if function.from == nil { importObjectBuilder.appendDts( [ - "\(renderTSPropertyName(jsName))\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: effects));" + "\(renderTSPropertyName(jsName))\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" ] ) } @@ -3337,11 +3339,12 @@ extension BridgeJSLink { for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: method.returnType) + let abiReturnType: BridgeType = method.effects.isAsync ? .jsObject(nil) : method.returnType + let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: abiReturnType) let funcLines = thunkBuilder.renderFunction( name: method.abiName(context: context), returnExpr: returnExpr, - returnType: method.returnType + returnType: abiReturnType ) return (funcLines, []) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 1f03e09ba..9eea98edb 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -923,6 +923,7 @@ public struct ImportedFunctionSkeleton: Codable { public let from: JSImportFrom? public let parameters: [Parameter] public let returnType: BridgeType + public let effects: Effects public let documentation: String? public init( @@ -931,6 +932,7 @@ public struct ImportedFunctionSkeleton: Codable { from: JSImportFrom? = nil, parameters: [Parameter], returnType: BridgeType, + effects: Effects = Effects(isAsync: false, isThrows: true), documentation: String? = nil ) { self.name = name @@ -938,6 +940,7 @@ public struct ImportedFunctionSkeleton: Codable { self.from = from self.parameters = parameters self.returnType = returnType + self.effects = effects self.documentation = documentation } diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js index 9617a5261..91a42a9ef 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js @@ -313,8 +313,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, decl); - const returnType = this.visitType(signature.getReturnType(), decl); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), decl); + const effects = this.renderEffects({ isAsync }); const annotation = this.renderMacroAnnotation("JSFunction", args); this.emitDocComment(decl, { indent: "", parameterNameMap }); @@ -581,8 +581,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, node); - const returnType = this.visitType(signature.getReturnType(), node); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), node); + const effects = this.renderEffects({ isAsync }); const swiftFuncName = this.renderIdentifier(swiftName); this.emitDocComment(node, { parameterNameMap }); @@ -1210,8 +1210,8 @@ export class TypeProcessor { const parameters = signature.getParameters(); const parameterNameMap = this.buildParameterNameMap(parameters); const params = this.renderParameters(parameters, node); - const returnType = this.visitType(signature.getReturnType(), node); - const effects = this.renderEffects({ isAsync: false }); + const { returnType, isAsync } = this.unwrapPromiseReturnType(signature.getReturnType(), node); + const effects = this.renderEffects({ isAsync }); const swiftMethodName = this.renderIdentifier(swiftName); const isStatic = node.modifiers?.some( (modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword @@ -1281,6 +1281,27 @@ export class TypeProcessor { return parts.join(" "); } + /** + * Check if a type is Promise and extract the return type and async flag. + * @param {ts.Type} type - The return type to check + * @param {ts.Node} node - The node for type visiting context + * @returns {{ returnType: string, isAsync: boolean }} + * @private + */ + unwrapPromiseReturnType(type, node) { + if (isTypeReference(type)) { + const symbol = type.target?.getSymbol(); + if (symbol?.name === "Promise") { + const typeArgs = this.checker.getTypeArguments(/** @type {ts.TypeReference} */ (type)); + const innerType = typeArgs && typeArgs.length > 0 + ? this.visitType(typeArgs[0], node) + : "Void"; + return { returnType: innerType, isAsync: true }; + } + } + return { returnType: this.visitType(type, node), isAsync: false }; + } + /** * @param {ts.Node} node * @returns {boolean} diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap index 4122f4148..643ac8441 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap @@ -32,19 +32,19 @@ exports[`ts2swift > snapshots Swift output for Async.d.ts > Async 1`] = ` @_spi(BridgeJS) import JavaScriptKit -@JSFunction func asyncReturnVoid() throws(JSException) -> JSPromise +@JSFunction func asyncReturnVoid() async throws(JSException) -> Void -@JSFunction func asyncRoundTripInt(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripInt(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripString(_ v: String) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripString(_ v: String) async throws(JSException) -> String -@JSFunction func asyncRoundTripBool(_ v: Bool) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool -@JSFunction func asyncRoundTripFloat(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripFloat(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripDouble(_ v: Double) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double -@JSFunction func asyncRoundTripJSObject(_ v: JSValue) throws(JSException) -> JSPromise +@JSFunction func asyncRoundTripJSObject(_ v: JSValue) async throws(JSException) -> JSValue " `; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift new file mode 100644 index 000000000..02563cbdf --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncImport.swift @@ -0,0 +1,6 @@ +@JSFunction func asyncReturnVoid() async throws(JSException) +@JSFunction func asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int +@JSFunction func asyncRoundTripString(_ v: String) async throws(JSException) -> String +@JSFunction func asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool +@JSFunction func asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double +@JSFunction func asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json index 3664fa339..d071d8c52 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json @@ -1331,6 +1331,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkArray", "parameters" : [ { @@ -1349,6 +1354,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkArrayWithLength", "parameters" : [ { @@ -1375,6 +1385,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessNumbers", "parameters" : [ { @@ -1397,6 +1412,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importGetNumbers", "parameters" : [ @@ -1412,6 +1432,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importTransformNumbers", "parameters" : [ { @@ -1438,6 +1463,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessStrings", "parameters" : [ { @@ -1464,6 +1494,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importProcessBooleans", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json new file mode 100644 index 000000000..263578d20 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json @@ -0,0 +1,151 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncReturnVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripInt", + "parameters" : [ + { + "name" : "v", + "type" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + } + ], + "returnType" : { + "integer" : { + "_0" : { + "isSigned" : true, + "width" : "word" + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripString", + "parameters" : [ + { + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripBool", + "parameters" : [ + { + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripDouble", + "parameters" : [ + { + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncRoundTripJSObject", + "parameters" : [ + { + "name" : "v", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + + } + } + } + ], + "types" : [ + + ] + } + ] + }, + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift new file mode 100644 index 000000000..eed78f197 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -0,0 +1,138 @@ +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncReturnVoid") +fileprivate func bjs_asyncReturnVoid_extern() -> Int32 +#else +fileprivate func bjs_asyncReturnVoid_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncReturnVoid() -> Int32 { + return bjs_asyncReturnVoid_extern() +} + +func _$asyncReturnVoid() async throws(JSException) -> Void { + let ret = bjs_asyncReturnVoid() + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + _ = try await promise.value +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripInt") +fileprivate func bjs_asyncRoundTripInt_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_asyncRoundTripInt_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripInt(_ v: Int32) -> Int32 { + return bjs_asyncRoundTripInt_extern(v) +} + +func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_asyncRoundTripInt(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return Int(resolved.number!) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripString") +fileprivate func bjs_asyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 +#else +fileprivate func bjs_asyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripString(_ vBytes: Int32, _ vLength: Int32) -> Int32 { + return bjs_asyncRoundTripString_extern(vBytes, vLength) +} + +func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { + let ret0 = v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + let ret = bjs_asyncRoundTripString(vBytes, vLength) + return ret + } + let ret = ret0 + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return resolved.string! +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripBool") +fileprivate func bjs_asyncRoundTripBool_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_asyncRoundTripBool_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripBool(_ v: Int32) -> Int32 { + return bjs_asyncRoundTripBool_extern(v) +} + +func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_asyncRoundTripBool(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return resolved.boolean! +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripDouble") +fileprivate func bjs_asyncRoundTripDouble_extern(_ v: Float64) -> Int32 +#else +fileprivate func bjs_asyncRoundTripDouble_extern(_ v: Float64) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ v: Float64) -> Int32 { + return bjs_asyncRoundTripDouble_extern(v) +} + +func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_asyncRoundTripDouble(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return Double(resolved.number!) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripJSObject") +fileprivate func bjs_asyncRoundTripJSObject_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_asyncRoundTripJSObject_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ v: Int32) -> Int32 { + return bjs_asyncRoundTripJSObject_extern(v) +} + +func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_asyncRoundTripJSObject(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return resolved.object! +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json index 4d7495a7c..a0c2c80c6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "fetchNumber", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json index c740dc46f..ea707098d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json @@ -235,6 +235,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "importMirrorDictionary", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json index ba36405ba..fc4a7ae52 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json @@ -1526,6 +1526,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "takesFeatureFlag", "parameters" : [ { @@ -1545,6 +1550,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "returnsFeatureFlag", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json index bef9cbc88..15a20f72e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json @@ -269,6 +269,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt8", "parameters" : [ { @@ -293,6 +298,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt8", "parameters" : [ { @@ -317,6 +327,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt16", "parameters" : [ { @@ -341,6 +356,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt16", "parameters" : [ { @@ -365,6 +385,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt32", "parameters" : [ { @@ -389,6 +414,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt32", "parameters" : [ { @@ -413,6 +443,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripInt64", "parameters" : [ { @@ -437,6 +472,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripUInt64", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json index 55ac7dd70..f750fc6a5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json @@ -22,6 +22,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "log", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json index 5e002e34f..809a9ad99 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "jsName" : "parseInt", "name" : "parseInt", @@ -42,6 +47,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "log", "parameters" : [ { @@ -87,6 +97,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "close", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json index 7f79a8146..3f9cb8e32 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundtrip", "parameters" : [ { @@ -36,6 +41,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "logStrings", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json index 935f7a7f2..1ad99f397 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createWeirdObject", "parameters" : [ @@ -15,6 +20,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "createWeirdClass", "parameters" : [ @@ -100,6 +110,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "as", "parameters" : [ @@ -111,6 +126,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "try", "parameters" : [ @@ -218,6 +238,11 @@ "jsName" : "$Weird", "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "method-with-dashes", "name" : "method_with_dashes", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json index 689e86150..ef8eba9ba 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "returnAnimatable", "parameters" : [ @@ -49,6 +54,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "greet", "parameters" : [ @@ -60,6 +70,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "changeName", "parameters" : [ { @@ -100,6 +115,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "animate", "parameters" : [ { @@ -126,6 +146,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "getAnimations", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json index a8b64558f..18f7cfaac 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json @@ -12,6 +12,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -29,6 +34,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { @@ -47,6 +57,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -58,6 +73,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeDefault", "parameters" : [ @@ -69,6 +89,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "with-dashes", "name" : "dashed", "parameters" : [ @@ -107,6 +132,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json index 5bd83be27..fb8601ae7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json @@ -321,6 +321,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsEchoJSValue", "parameters" : [ { @@ -339,6 +344,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsEchoJSValueArray", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json index 67d97821c..3e6d6c60c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json @@ -1152,6 +1152,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripStringOrNull", "parameters" : [ { @@ -1180,6 +1185,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripStringOrUndefined", "parameters" : [ { @@ -1208,6 +1218,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripDoubleOrNull", "parameters" : [ { @@ -1236,6 +1251,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripDoubleOrUndefined", "parameters" : [ { @@ -1264,6 +1284,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripBoolOrNull", "parameters" : [ { @@ -1292,6 +1317,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripBoolOrUndefined", "parameters" : [ { @@ -1320,6 +1350,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripIntOrNull", "parameters" : [ { @@ -1354,6 +1389,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "roundTripIntOrUndefined", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json index f75bf7610..cf76f3878 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json @@ -88,6 +88,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "check", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json index cded9a973..b0398c161 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json @@ -112,6 +112,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkNumber", "parameters" : [ @@ -123,6 +128,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkBoolean", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json index b0aa8c35b..75462af81 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json @@ -71,6 +71,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkString", "parameters" : [ { @@ -89,6 +94,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkStringWithLength", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json index 3f9271592..1088a5cab 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json @@ -38,6 +38,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "checkString", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json index 7cebdd5e6..265b4036b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json @@ -146,6 +146,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripGreeter", "parameters" : [ { @@ -164,6 +169,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalGreeter", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json index 4359b50ec..0e348a44e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json @@ -4,6 +4,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "applyInt", "parameters" : [ { @@ -60,6 +65,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeAdder", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json index c1329cd79..ccd3043ac 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json @@ -56,6 +56,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "translate", "parameters" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json index 14da32841..7f19c18bf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json @@ -38,6 +38,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "check", "parameters" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts new file mode 100644 index 000000000..e612ae1e1 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.d.ts @@ -0,0 +1,23 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export type Exports = { +} +export type Imports = { + asyncReturnVoid(): Promise; + asyncRoundTripInt(v: number): Promise; + asyncRoundTripString(v: string): Promise; + asyncRoundTripBool(v: boolean): Promise; + asyncRoundTripDouble(v: number): Promise; + asyncRoundTripJSObject(v: any): Promise; +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js new file mode 100644 index 000000000..28943b686 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -0,0 +1,268 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid() { + try { + let ret = imports.asyncReturnVoid(); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(v) { + try { + let ret = imports.asyncRoundTripInt(v); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(vBytes, vCount) { + try { + const string = decodeString(vBytes, vCount); + let ret = imports.asyncRoundTripString(string); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(v) { + try { + let ret = imports.asyncRoundTripBool(v !== 0); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(v) { + try { + let ret = imports.asyncRoundTripDouble(v); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(v) { + try { + let ret = imports.asyncRoundTripJSObject(swift.memory.getObject(v)); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift new file mode 100644 index 000000000..31d7484ea --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -0,0 +1,23 @@ +import Testing +import JavaScriptKit + +@Suite struct AsyncImportTests { + @Test func asyncRoundTripVoid() async throws { + try await jsAsyncRoundTripVoid() + } + + @Test(arguments: [0.0, 1.0, -1.0, Double.pi, Double.infinity]) + func asyncRoundTripNumber(v: Double) async throws { + try #expect(await jsAsyncRoundTripNumber(v) == v) + } + + @Test(arguments: [true, false]) + func asyncRoundTripBool(v: Bool) async throws { + try #expect(await jsAsyncRoundTripBool(v) == v) + } + + @Test(arguments: ["", "Hello, world!", "🧑‍🧑‍🧒"]) + func asyncRoundTripString(v: String) async throws { + try #expect(await jsAsyncRoundTripString(v) == v) + } +} diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 595f6c051..014170bf3 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -1279,6 +1279,6 @@ class ExportAPITests: XCTestCase { } func testAllAsync() async throws { - _ = try await runAsyncWorks().value() + try await runAsyncWorks() } } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index ac9ad0bc6..063c3e06b 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -41,7 +41,15 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSFunction func changeName(_ name: String) throws(JSException) -> Void } -@JSFunction func runAsyncWorks() throws(JSException) -> JSPromise +@JSFunction func runAsyncWorks() async throws(JSException) -> Void + +@JSFunction func jsAsyncRoundTripVoid() async throws(JSException) -> Void + +@JSFunction func jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double + +@JSFunction func jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool + +@JSFunction func jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String @JSFunction(jsName: "$jsWeirdFunction") func _jsWeirdFunction() throws(JSException) -> Double diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index e01761a8e..847abe99b 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -11137,12 +11137,106 @@ fileprivate func bjs_runAsyncWorks_extern() -> Int32 { return bjs_runAsyncWorks_extern() } -func _$runAsyncWorks() throws(JSException) -> JSPromise { +func _$runAsyncWorks() async throws(JSException) -> Void { let ret = bjs_runAsyncWorks() if let error = _swift_js_take_exception() { throw error } - return JSPromise.bridgeJSLiftReturn(ret) + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + _ = try await promise.value +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripVoid") +fileprivate func bjs_jsAsyncRoundTripVoid_extern() -> Int32 +#else +fileprivate func bjs_jsAsyncRoundTripVoid_extern() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripVoid() -> Int32 { + return bjs_jsAsyncRoundTripVoid_extern() +} + +func _$jsAsyncRoundTripVoid() async throws(JSException) -> Void { + let ret = bjs_jsAsyncRoundTripVoid() + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + _ = try await promise.value +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripNumber") +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ v: Float64) -> Int32 +#else +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ v: Float64) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripNumber(_ v: Float64) -> Int32 { + return bjs_jsAsyncRoundTripNumber_extern(v) +} + +func _$jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_jsAsyncRoundTripNumber(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return Double(resolved.number!) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBool") +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ v: Int32) -> Int32 +#else +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripBool(_ v: Int32) -> Int32 { + return bjs_jsAsyncRoundTripBool_extern(v) +} + +func _$jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { + let vValue = v.bridgeJSLowerParameter() + let ret = bjs_jsAsyncRoundTripBool(vValue) + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return resolved.boolean! +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripString") +fileprivate func bjs_jsAsyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 +#else +fileprivate func bjs_jsAsyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripString(_ vBytes: Int32, _ vLength: Int32) -> Int32 { + return bjs_jsAsyncRoundTripString_extern(vBytes, vLength) +} + +func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { + let ret0 = v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + let ret = bjs_jsAsyncRoundTripString(vBytes, vLength) + return ret + } + let ret = ret0 + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return resolved.string! } #if arch(wasm32) diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index cd8111566..0ed7ef945 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -16118,6 +16118,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsIntArrayLength", "parameters" : [ { @@ -16146,6 +16151,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripIntArray", "parameters" : [ { @@ -16178,6 +16188,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripNumberArray", "parameters" : [ { @@ -16204,6 +16219,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripStringArray", "parameters" : [ { @@ -16230,6 +16250,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripBoolArray", "parameters" : [ { @@ -16256,6 +16281,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSValueArray", "parameters" : [ { @@ -16282,6 +16312,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSObjectArray", "parameters" : [ { @@ -16308,6 +16343,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSClassArray", "parameters" : [ { @@ -16334,6 +16374,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalIntArray", "parameters" : [ { @@ -16376,6 +16421,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringArray", "parameters" : [ { @@ -16412,6 +16462,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalBoolArray", "parameters" : [ { @@ -16448,6 +16503,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArray", "parameters" : [ { @@ -16484,6 +16544,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSObjectArray", "parameters" : [ { @@ -16520,6 +16585,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSClassArray", "parameters" : [ { @@ -16556,6 +16626,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsSumNumberArray", "parameters" : [ { @@ -16578,6 +16653,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCreateNumberArray", "parameters" : [ @@ -16593,6 +16673,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsArraySupportTests", "parameters" : [ @@ -16625,6 +16710,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyVoid", "parameters" : [ { @@ -16657,6 +16747,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyBool", "parameters" : [ { @@ -16689,6 +16784,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyInt", "parameters" : [ { @@ -16745,6 +16845,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyDouble", "parameters" : [ { @@ -16789,6 +16894,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyString", "parameters" : [ { @@ -16833,6 +16943,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsApplyJSObject", "parameters" : [ { @@ -16877,6 +16992,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeIntToInt", "parameters" : [ { @@ -16922,6 +17042,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeDoubleToDouble", "parameters" : [ { @@ -16958,6 +17083,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsMakeStringToString", "parameters" : [ { @@ -16994,6 +17124,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallTwice", "parameters" : [ { @@ -17047,6 +17182,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallBinary", "parameters" : [ { @@ -17100,6 +17240,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallTriple", "parameters" : [ { @@ -17161,6 +17306,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallAfterRelease", "parameters" : [ { @@ -17193,6 +17343,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsOptionalInvoke", "parameters" : [ { @@ -17230,6 +17385,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsStoreClosure", "parameters" : [ { @@ -17262,6 +17422,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsCallStoredClosure", "parameters" : [ @@ -17273,6 +17438,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsHeapCount", "parameters" : [ @@ -17287,6 +17457,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsClosureSupportTests", "parameters" : [ @@ -17319,6 +17494,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsDefaultArgumentTests", "parameters" : [ @@ -17351,6 +17531,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryInt", "parameters" : [ { @@ -17383,6 +17568,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryBool", "parameters" : [ { @@ -17409,6 +17599,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryDouble", "parameters" : [ { @@ -17435,6 +17630,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryJSObject", "parameters" : [ { @@ -17461,6 +17661,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryJSValue", "parameters" : [ { @@ -17487,6 +17692,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripDictionaryDoubleArray", "parameters" : [ { @@ -17568,6 +17778,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripVoid", "parameters" : [ @@ -17579,6 +17794,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripNumber", "parameters" : [ { @@ -17597,6 +17817,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripBool", "parameters" : [ { @@ -17615,6 +17840,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripString", "parameters" : [ { @@ -17633,6 +17863,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripJSValue", "parameters" : [ { @@ -17651,6 +17886,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrVoid", "parameters" : [ { @@ -17669,6 +17909,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrNumber", "parameters" : [ { @@ -17687,6 +17932,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrBool", "parameters" : [ { @@ -17705,6 +17955,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsThrowOrString", "parameters" : [ { @@ -17723,6 +17978,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripFeatureFlag", "parameters" : [ { @@ -17743,17 +18003,112 @@ } }, { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, "name" : "runAsyncWorks", "parameters" : [ ], "returnType" : { - "jsObject" : { - "_0" : "JSPromise" + "void" : { + } } }, { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripNumber", + "parameters" : [ + { + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripBool", + "parameters" : [ + { + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripString", + "parameters" : [ + { + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "$jsWeirdFunction", "name" : "_jsWeirdFunction", "parameters" : [ @@ -17766,6 +18121,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "name" : "parseInt", "parameters" : [ @@ -17838,6 +18198,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "greet", "parameters" : [ @@ -17849,6 +18214,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "changeName", "parameters" : [ { @@ -17895,6 +18265,11 @@ "jsName" : "$WeirdClass", "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "method-with-dashes", "name" : "method_with_dashes", "parameters" : [ @@ -17933,6 +18308,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -17950,6 +18330,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "create", "parameters" : [ { @@ -17968,6 +18353,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "value", "parameters" : [ @@ -17979,6 +18369,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeDefault", "parameters" : [ @@ -17990,6 +18385,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "jsName" : "with-dashes", "name" : "with_dashes", "parameters" : [ @@ -18061,6 +18461,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "bark", "parameters" : [ @@ -18072,6 +18477,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "getIsCat", "parameters" : [ @@ -18122,6 +18532,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsTranslatePoint", "parameters" : [ { @@ -18184,6 +18599,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt", "parameters" : [ { @@ -18208,6 +18628,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt", "parameters" : [ { @@ -18232,6 +18657,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt8", "parameters" : [ { @@ -18256,6 +18686,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt8", "parameters" : [ { @@ -18280,6 +18715,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt16", "parameters" : [ { @@ -18304,6 +18744,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt16", "parameters" : [ { @@ -18328,6 +18773,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt32", "parameters" : [ { @@ -18352,6 +18802,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt32", "parameters" : [ { @@ -18376,6 +18831,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripInt64", "parameters" : [ { @@ -18400,6 +18860,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUInt64", "parameters" : [ { @@ -18424,6 +18889,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsIntegerTypesSupportTests", "parameters" : [ @@ -18506,6 +18976,11 @@ ], "methods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "concatNumbers", "parameters" : [ { @@ -18538,6 +19013,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "concatLabels", "parameters" : [ { @@ -18564,6 +19044,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "firstLabel", "parameters" : [ { @@ -18635,6 +19120,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "makeJSClassWithArrayMembers", "parameters" : [ { @@ -18748,6 +19238,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPackageAccess", "parameters" : [ @@ -18759,6 +19254,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPublicAccess", "parameters" : [ @@ -18770,6 +19270,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithInternalAccess", "parameters" : [ @@ -18781,6 +19286,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithFilePrivateAccess", "parameters" : [ @@ -18792,6 +19302,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsFunctionWithPrivateAccess", "parameters" : [ @@ -18825,6 +19340,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalNumberNull", "parameters" : [ { @@ -18859,6 +19379,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalNumberUndefined", "parameters" : [ { @@ -18893,6 +19418,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringNull", "parameters" : [ { @@ -18921,6 +19451,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringUndefined", "parameters" : [ { @@ -18949,6 +19484,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArrayNull", "parameters" : [ { @@ -18985,6 +19525,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalJSValueArrayUndefined", "parameters" : [ { @@ -19021,6 +19566,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringToStringDictionaryNull", "parameters" : [ { @@ -19057,6 +19607,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalStringToStringDictionaryUndefined", "parameters" : [ { @@ -19093,6 +19648,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "runJsOptionalSupportTests", "parameters" : [ @@ -19110,6 +19670,11 @@ { "functions" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "from" : "global", "name" : "gc", "parameters" : [ @@ -19136,6 +19701,11 @@ ], "staticMethods" : [ { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripGreeter", "parameters" : [ { @@ -19154,6 +19724,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripUUID", "parameters" : [ { @@ -19172,6 +19747,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsRoundTripOptionalGreeter", "parameters" : [ { @@ -19200,6 +19780,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsConsumeLeakCheck", "parameters" : [ { @@ -19218,6 +19803,11 @@ } }, { + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, "name" : "jsConsumeOptionalLeakCheck", "parameters" : [ { diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index e8533a3d8..a4529c4f4 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -26,6 +26,12 @@ export class JsGreeter { export function runAsyncWorks(): Promise; +// Async round-trip tests +export function jsAsyncRoundTripVoid(): Promise; +export function jsAsyncRoundTripNumber(v: number): Promise; +export function jsAsyncRoundTripBool(v: boolean): Promise; +export function jsAsyncRoundTripString(v: string): Promise; + // jsName tests export function $jsWeirdFunction(): number; diff --git a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift index db093e549..e3c19a8e4 100644 --- a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift +++ b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift @@ -72,7 +72,7 @@ class JSClosureAsyncTests: XCTestCase { )!.value() XCTAssertEqual(result, 42.0) } - + func testAsyncOneshotClosureWithPriority() async throws { let priority = UnsafeSendableBox(nil) let closure = JSOneshotClosure.async(priority: .high) { _ in @@ -83,7 +83,7 @@ class JSClosureAsyncTests: XCTestCase { XCTAssertEqual(result, 42.0) XCTAssertEqual(priority.value, .high) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutor() async throws { let executor = AnyTaskExecutor() @@ -93,7 +93,7 @@ class JSClosureAsyncTests: XCTestCase { let result = try await JSPromise(from: closure.function!())!.value() XCTAssertEqual(result, 42.0) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutorPreference() async throws { let executor = AnyTaskExecutor() diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 265781bff..cbda57f27 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -119,6 +119,18 @@ export async function setupOptions(options, context) { }, StaticBox, Foo: ImportedFoo, + "jsAsyncRoundTripVoid": () => { + return Promise.resolve(); + }, + "jsAsyncRoundTripNumber": (v) => { + return Promise.resolve(v); + }, + "jsAsyncRoundTripBool": (v) => { + return Promise.resolve(v); + }, + "jsAsyncRoundTripString": (v) => { + return Promise.resolve(v); + }, runAsyncWorks: async () => { const exports = importsContext.getExports(); if (!exports) { From d741dce41ad0b3dd55cbf818d40bcb2f35595d9d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 23 Mar 2026 11:32:06 +0000 Subject: [PATCH 02/10] E2e testing of bridging Promise returns --- .../Sources/BridgeJSCore/ImportTS.swift | 8 +- .../AsyncImportTests.swift | 12 ++ .../Generated/BridgeJS.Macros.swift | 11 ++ .../Generated/BridgeJS.swift | 153 ++++++++++++++++++ .../Generated/JavaScript/BridgeJS.json | 87 ++++++++++ Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 8 + Tests/prelude.mjs | 7 + 7 files changed, 284 insertions(+), 2 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 47880b654..ca0a2224c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -301,8 +301,12 @@ public struct ImportTS { liftExpr = "resolved.string!" case .bool: liftExpr = "resolved.boolean!" - case .jsObject: - liftExpr = "resolved.object!" + case .jsObject(let name): + if let name { + liftExpr = "\(name)(unsafelyWrapping: resolved.object!)" + } else { + liftExpr = "resolved.object!" + } case .jsValue: liftExpr = "resolved" default: diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift index 31d7484ea..9b6cc0688 100644 --- a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -20,4 +20,16 @@ import JavaScriptKit func asyncRoundTripString(v: String) async throws { try #expect(await jsAsyncRoundTripString(v) == v) } + + @Test func fetchWeatherData() async throws { + let weather = try await BridgeJSRuntimeTests.fetchWeatherData("London") + #expect(try weather.temperature == 15.5) + #expect(try weather.description == "Cloudy") + #expect(try weather.humidity == 80) + + let weather2 = try await BridgeJSRuntimeTests.fetchWeatherData("Tokyo") + #expect(try weather2.temperature == 25.0) + #expect(try weather2.description == "Sunny") + #expect(try weather2.humidity == 40) + } } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index 063c3e06b..b4e780bee 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -51,6 +51,17 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSFunction func jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String +@JSFunction func fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData + +@JSClass struct WeatherData { + @JSGetter var temperature: Double + @JSSetter func setTemperature(_ value: Double) throws(JSException) + @JSGetter var description: String + @JSSetter func setDescription(_ value: String) throws(JSException) + @JSGetter var humidity: Double + @JSSetter func setHumidity(_ value: Double) throws(JSException) +} + @JSFunction(jsName: "$jsWeirdFunction") func _jsWeirdFunction() throws(JSException) -> Double @JSClass(jsName: "$WeirdClass") struct _WeirdClass { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 847abe99b..a1990ab0c 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -11239,6 +11239,32 @@ func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { return resolved.string! } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") +fileprivate func bjs_fetchWeatherData_extern(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 +#else +fileprivate func bjs_fetchWeatherData_extern(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_fetchWeatherData(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 { + return bjs_fetchWeatherData_extern(cityBytes, cityLength) +} + +func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { + let ret0 = city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in + let ret = bjs_fetchWeatherData(cityBytes, cityLength) + return ret + } + let ret = ret0 + if let error = _swift_js_take_exception() { + throw error + } + let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) + let resolved = try await promise.value + return WeatherData(unsafelyWrapping: resolved.object!) +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__jsWeirdFunction") fileprivate func bjs__jsWeirdFunction_extern() -> Float64 @@ -11417,6 +11443,133 @@ func _$JsGreeter_changeName(_ self: JSObject, _ name: String) throws(JSException } } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_temperature_get") +fileprivate func bjs_WeatherData_temperature_get_extern(_ self: Int32) -> Float64 +#else +fileprivate func bjs_WeatherData_temperature_get_extern(_ self: Int32) -> Float64 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_temperature_get(_ self: Int32) -> Float64 { + return bjs_WeatherData_temperature_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_description_get") +fileprivate func bjs_WeatherData_description_get_extern(_ self: Int32) -> Int32 +#else +fileprivate func bjs_WeatherData_description_get_extern(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_description_get(_ self: Int32) -> Int32 { + return bjs_WeatherData_description_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_humidity_get") +fileprivate func bjs_WeatherData_humidity_get_extern(_ self: Int32) -> Float64 +#else +fileprivate func bjs_WeatherData_humidity_get_extern(_ self: Int32) -> Float64 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_humidity_get(_ self: Int32) -> Float64 { + return bjs_WeatherData_humidity_get_extern(self) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_temperature_set") +fileprivate func bjs_WeatherData_temperature_set_extern(_ self: Int32, _ newValue: Float64) -> Void +#else +fileprivate func bjs_WeatherData_temperature_set_extern(_ self: Int32, _ newValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_temperature_set(_ self: Int32, _ newValue: Float64) -> Void { + return bjs_WeatherData_temperature_set_extern(self, newValue) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_description_set") +fileprivate func bjs_WeatherData_description_set_extern(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void +#else +fileprivate func bjs_WeatherData_description_set_extern(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_description_set(_ self: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void { + return bjs_WeatherData_description_set_extern(self, newValueBytes, newValueLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_WeatherData_humidity_set") +fileprivate func bjs_WeatherData_humidity_set_extern(_ self: Int32, _ newValue: Float64) -> Void +#else +fileprivate func bjs_WeatherData_humidity_set_extern(_ self: Int32, _ newValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_WeatherData_humidity_set(_ self: Int32, _ newValue: Float64) -> Void { + return bjs_WeatherData_humidity_set_extern(self, newValue) +} + +func _$WeatherData_temperature_get(_ self: JSObject) throws(JSException) -> Double { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_temperature_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_description_get(_ self: JSObject) throws(JSException) -> String { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_description_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return String.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_humidity_get(_ self: JSObject) throws(JSException) -> Double { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WeatherData_humidity_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +func _$WeatherData_temperature_set(_ self: JSObject, _ newValue: Double) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let newValueValue = newValue.bridgeJSLowerParameter() + bjs_WeatherData_temperature_set(selfValue, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WeatherData_description_set(_ self: JSObject, _ newValue: String) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + newValue.bridgeJSWithLoweredParameter { (newValueBytes, newValueLength) in + bjs_WeatherData_description_set(selfValue, newValueBytes, newValueLength) + } + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WeatherData_humidity_set(_ self: JSObject, _ newValue: Double) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let newValueValue = newValue.bridgeJSLowerParameter() + bjs_WeatherData_humidity_set(selfValue, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__WeirdClass_init") fileprivate func bjs__WeirdClass_init_extern() -> Int32 diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 0ed7ef945..9ef5ec887 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -18103,6 +18103,29 @@ } } }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "fetchWeatherData", + "parameters" : [ + { + "name" : "city", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "WeatherData" + } + } + }, { "effects" : { "isAsync" : false, @@ -18253,6 +18276,70 @@ ] }, + { + "getters" : [ + { + "name" : "temperature", + "type" : { + "double" : { + + } + } + }, + { + "name" : "description", + "type" : { + "string" : { + + } + } + }, + { + "name" : "humidity", + "type" : { + "double" : { + + } + } + } + ], + "methods" : [ + + ], + "name" : "WeatherData", + "setters" : [ + { + "functionName" : "temperature_set", + "name" : "temperature", + "type" : { + "double" : { + + } + } + }, + { + "functionName" : "description_set", + "name" : "description", + "type" : { + "string" : { + + } + } + }, + { + "functionName" : "humidity_set", + "name" : "humidity", + "type" : { + "double" : { + + } + } + } + ], + "staticMethods" : [ + + ] + }, { "constructor" : { "parameters" : [ diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index a4529c4f4..635a783b8 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -32,6 +32,14 @@ export function jsAsyncRoundTripNumber(v: number): Promise; export function jsAsyncRoundTripBool(v: boolean): Promise; export function jsAsyncRoundTripString(v: string): Promise; +// Async fetch-like test with structured return type +export interface WeatherData { + temperature: number; + description: string; + humidity: number; +} +export function fetchWeatherData(city: string): Promise; + // jsName tests export function $jsWeirdFunction(): number; diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index cbda57f27..e18bdfd12 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -119,6 +119,13 @@ export async function setupOptions(options, context) { }, StaticBox, Foo: ImportedFoo, + "fetchWeatherData": (city) => { + return Promise.resolve({ + temperature: city === "London" ? 15.5 : 25.0, + description: city === "London" ? "Cloudy" : "Sunny", + humidity: city === "London" ? 80 : 40, + }); + }, "jsAsyncRoundTripVoid": () => { return Promise.resolve(); }, From d0b207c964ee47759f79595720fd68e5763ec709 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 23 Mar 2026 23:15:45 +0000 Subject: [PATCH 03/10] fix formatting --- Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift index e3c19a8e4..db093e549 100644 --- a/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift +++ b/Tests/JavaScriptEventLoopTests/JSClosure+AsyncTests.swift @@ -72,7 +72,7 @@ class JSClosureAsyncTests: XCTestCase { )!.value() XCTAssertEqual(result, 42.0) } - + func testAsyncOneshotClosureWithPriority() async throws { let priority = UnsafeSendableBox(nil) let closure = JSOneshotClosure.async(priority: .high) { _ in @@ -83,7 +83,7 @@ class JSClosureAsyncTests: XCTestCase { XCTAssertEqual(result, 42.0) XCTAssertEqual(priority.value, .high) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutor() async throws { let executor = AnyTaskExecutor() @@ -93,7 +93,7 @@ class JSClosureAsyncTests: XCTestCase { let result = try await JSPromise(from: closure.function!())!.value() XCTAssertEqual(result, 42.0) } - + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testAsyncOneshotClosureWithTaskExecutorPreference() async throws { let executor = AnyTaskExecutor() From 2b8bcaf688a871b0d87066525276cd2706849f71 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 11:20:07 +0000 Subject: [PATCH 04/10] `JSTypedClosure`-based approach --- .../Sources/BridgeJSCore/ImportTS.swift | 61 ++++-- .../Sources/BridgeJSLink/BridgeJSLink.swift | 200 ++++++++++++++++-- .../BridgeJSCodegenTests/AsyncImport.swift | 104 ++++----- .../BridgeJSLinkTests/AsyncImport.js | 146 ++++++++++--- .../JavaScriptKit/BridgeJSIntrinsics.swift | 88 ++++++++ .../Generated/BridgeJS.swift | 106 ++++------ 6 files changed, 512 insertions(+), 193 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index ca0a2224c..3e4826904 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -212,7 +212,16 @@ public struct ImportTS { } } - func call() throws { + /// Prepends a `continuationPtr: Int32` parameter to the ABI parameter list. + /// + /// Used for async imports where the JS side needs the continuation pointer + /// to resolve/reject the Promise. + func prependContinuationPtr() { + abiParameterSignatures.insert(("continuationPtr", .i32), at: 0) + abiParameterForwardings.insert("continuationPtr", at: 0) + } + + func call(skipExceptionCheck: Bool = false) throws { for stmt in stackLoweringStmts { body.write(stmt.description) } @@ -243,8 +252,9 @@ public struct ImportTS { } } - // Add exception check for ImportTS context - if context == .importTS { + // Add exception check for ImportTS context (skipped for async, where + // errors are funneled through the JS-side reject path) + if !skipExceptionCheck && context == .importTS { body.write("if let error = _swift_js_take_exception() { throw error }") } } @@ -279,16 +289,27 @@ public struct ImportTS { } func liftAsyncReturnValue(originalReturnType: BridgeType) { - // For async imports, the extern function returns a Promise object ID (i32). - // We wrap it in JSPromise, await the resolved value, then lift to the target type. - abiReturnType = .i32 - body.write( - "let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret)))" - ) + // For async imports, we use the continuation-pointer pattern. + // The extern function takes a leading `continuationPtr: Int32` and returns void. + // The JS side attaches .then/.catch handlers and calls back into Wasm + // via bjs_resolve_promise_continuation / bjs_reject_promise_continuation. + abiReturnType = nil + + // Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise + let innerBody = body + body = CodeFragmentPrinter() + if originalReturnType == .void { - body.write("_ = try await promise.value") + body.write("_ = try await _bjs_awaitPromise { continuationPtr in") } else { - body.write("let resolved = try await promise.value") + body.write("let resolved = try await _bjs_awaitPromise { continuationPtr in") + } + body.indent { + body.write(lines: innerBody.lines) + } + body.write("}") + + if originalReturnType != .void { let liftExpr: String switch originalReturnType { case .double: @@ -401,18 +422,21 @@ public struct ImportTS { _ function: ImportedFunctionSkeleton, topLevelDecls: inout [DeclSyntax] ) throws -> [DeclSyntax] { - // For async functions, the ABI return type is always jsObject (the Promise). - // We tell CallJSEmission that the return type is jsObject so it captures the return value. - let abiReturnType: BridgeType = function.effects.isAsync ? .jsObject(nil) : function.returnType + // For async functions, the extern returns void (the JS side resolves/rejects + // via continuation callbacks). For sync functions, use the actual return type. + let abiReturnType: BridgeType = function.effects.isAsync ? .void : function.returnType let builder = try CallJSEmission( moduleName: moduleName, abiName: function.abiName(context: nil), returnType: abiReturnType ) + if function.effects.isAsync { + builder.prependContinuationPtr() + } for param in function.parameters { try builder.lowerParameter(param: param) } - try builder.call() + try builder.call(skipExceptionCheck: function.effects.isAsync) if function.effects.isAsync { builder.liftAsyncReturnValue(originalReturnType: function.returnType) } else { @@ -435,17 +459,20 @@ public struct ImportTS { var decls: [DeclSyntax] = [] func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { - let abiReturnType: BridgeType = method.effects.isAsync ? .jsObject(nil) : method.returnType + let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType let builder = try CallJSEmission( moduleName: moduleName, abiName: method.abiName(context: type), returnType: abiReturnType ) + if method.effects.isAsync { + builder.prependContinuationPtr() + } try builder.lowerParameter(param: selfParameter) for param in method.parameters { try builder.lowerParameter(param: param) } - try builder.call() + try builder.call(skipExceptionCheck: method.effects.isAsync) if method.effects.isAsync { builder.liftAsyncReturnValue(originalReturnType: method.returnType) } else { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index e1f18183e..a8d7e5e43 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -135,6 +135,7 @@ public struct BridgeJSLink { var importObjectBuilders: [ImportObjectBuilder] = [] var enumStaticAssignments: [String] = [] var needsImportsObject: Bool = false + var hasAsyncImports: Bool = false } private func collectLinkData() throws -> LinkData { @@ -237,12 +238,19 @@ public struct BridgeJSLink { if function.from == nil { data.needsImportsObject = true } + if function.effects.isAsync { + data.hasAsyncImports = true + } try renderImportedFunction(importObjectBuilder: importObjectBuilder, function: function) } for type in fileSkeleton.types { if type.constructor != nil, type.from == nil { data.needsImportsObject = true } + // Check for async methods in imported types + for method in type.methods where method.effects.isAsync { + data.hasAsyncImports = true + } try renderImportedType(importObjectBuilder: importObjectBuilder, type: type) } } @@ -314,6 +322,85 @@ public struct BridgeJSLink { ] } + /// Generates helper functions for the continuation-pointer pattern used by async imports. + /// + /// These encode a JS value as `(kind, payload1, payload2)` matching the `RawJSValue` + /// encoding from `_CJavaScriptKit.h`, then call the appropriate Wasm export to resume + /// the Swift continuation. + private func generatePromiseContinuationHelpers() -> [String] { + let printer = CodeFragmentPrinter() + // Helper to encode a JS value into (kind, payload1, payload2) and call the resolve export + printer.write("function bjs_resolvePromiseContinuation(ptr, value) {") + printer.indent { + printer.write(lines: generateJSValueEncoding(variableName: "value")) + printer.write( + "\(JSGlueVariableScope.reservedInstance).exports.bjs_resolve_promise_continuation(ptr, kind, payload1, payload2);" + ) + } + printer.write("}") + // Helper to encode a JS value into (kind, payload1, payload2) and call the reject export + printer.write("function bjs_rejectPromiseContinuation(ptr, error) {") + printer.indent { + printer.write(lines: generateJSValueEncoding(variableName: "error")) + printer.write( + "\(JSGlueVariableScope.reservedInstance).exports.bjs_reject_promise_continuation(ptr, kind, payload1, payload2);" + ) + } + printer.write("}") + return printer.lines + } + + /// Generates JS code that encodes a variable into `(kind, payload1, payload2)`. + /// + /// The encoding matches `JavaScriptValueKind` from `_CJavaScriptKit.h`: + /// - Boolean(0): payload1 = 1 or 0 + /// - String(1): payload1 = retained object ID + /// - Number(2): payload2 = the number + /// - Object(3): payload1 = retained object ID + /// - Null(4): no payload + /// - Undefined(5): no payload + /// - Symbol(7): payload1 = retained object ID + /// - BigInt(8): payload1 = retained object ID + private func generateJSValueEncoding(variableName: String) -> [String] { + let s = JSGlueVariableScope.reservedSwift + return [ + "let kind, payload1 = 0, payload2 = 0;", + "if (\(variableName) === null) {", + " kind = 4;", + "} else if (\(variableName) === undefined) {", + " kind = 5;", + "} else {", + " const type = typeof \(variableName);", + " switch (type) {", + " case \"boolean\":", + " kind = 0;", + " payload1 = \(variableName) ? 1 : 0;", + " break;", + " case \"number\":", + " kind = 2;", + " payload2 = \(variableName);", + " break;", + " case \"string\":", + " kind = 1;", + " payload1 = \(s).memory.retain(\(variableName));", + " break;", + " case \"symbol\":", + " kind = 7;", + " payload1 = \(s).memory.retain(\(variableName));", + " break;", + " case \"bigint\":", + " kind = 8;", + " payload1 = \(s).memory.retain(\(variableName));", + " break;", + " default:", + " kind = 3;", + " payload1 = \(s).memory.retain(\(variableName));", + " break;", + " }", + "}", + ] + } + private func generateAddImports(needsImportsObject: Bool) throws -> CodeFragmentPrinter { let printer = CodeFragmentPrinter() let allStructs = skeletons.compactMap { $0.exported?.structs }.flatMap { $0 } @@ -970,6 +1057,11 @@ public struct BridgeJSLink { try printer.indent { printer.write(lines: generateVariableDeclarations()) + // Generate Promise continuation helpers when async imports exist + if data.hasAsyncImports { + printer.write(lines: generatePromiseContinuationHelpers()) + } + let bodyPrinter = CodeFragmentPrinter() let allStructs = exportedSkeletons.flatMap { $0.structs } for structDef in allStructs { @@ -2232,6 +2324,46 @@ extension BridgeJSLink { return printer.lines } + /// Generates the call expression for an async import. + /// + /// Instead of lowering the return value, this assigns the result to `promise` + /// and attaches `.then`/`.catch` handlers that call the resolve/reject continuations. + func callAsync(name: String, fromObjectExpr: String) { + let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) + let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" + body.write("const promise = \(callExpr);") + body.write("promise.then(") + body.indent { + body.write("(value) => { bjs_resolvePromiseContinuation(continuationPtr, value); },") + body.write("(error) => { bjs_rejectPromiseContinuation(continuationPtr, error); }") + } + body.write(");") + } + + /// Renders an async import function with continuation-pointer pattern. + /// + /// The generated function takes `continuationPtr` as the first parameter, + /// wraps the import call in try/catch, attaches Promise handlers, and + /// calls reject continuation on synchronous errors. + func renderAsyncFunction(name: String?) -> [String] { + let printer = CodeFragmentPrinter() + let allParams = ["continuationPtr"] + parameterNames + printer.write("function\(name.map { " \($0)" } ?? "")(\(allParams.joined(separator: ", "))) {") + printer.indent { + printer.write("try {") + printer.indent { + printer.write(contentsOf: body) + } + printer.write("} catch (error) {") + printer.indent { + printer.write("bjs_rejectPromiseContinuation(continuationPtr, error);") + } + printer.write("}") + } + printer.write("}") + return printer.lines + } + func call(name: String, fromObjectExpr: String, returnType: BridgeType) throws -> String? { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) return try self.call(calleeExpr: calleeExpr, returnType: returnType) @@ -2285,6 +2417,20 @@ extension BridgeJSLink { ) } + /// Generates an async method call with continuation-pointer pattern. + func callAsyncMethod(name: String) { + let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" + let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) + let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" + body.write("const promise = \(callExpr);") + body.write("promise.then(") + body.indent { + body.write("(value) => { bjs_resolvePromiseContinuation(continuationPtr, value); },") + body.write("(error) => { bjs_rejectPromiseContinuation(continuationPtr, error); }") + } + body.write(");") + } + func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws -> String? { let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) return try call( @@ -3124,19 +3270,27 @@ extension BridgeJSLink { } let jsName = function.jsName ?? function.name let importRootExpr = function.from == .global ? "globalThis" : "imports" - // For async functions, the JS handler returns the Promise as a jsObject. - // The Swift side handles awaiting and lifting the resolved value. - let abiReturnType: BridgeType = function.effects.isAsync ? .jsObject(nil) : function.returnType - let returnExpr = try thunkBuilder.call( - name: jsName, - fromObjectExpr: importRootExpr, - returnType: abiReturnType - ) - let funcLines = thunkBuilder.renderFunction( - name: function.abiName(context: nil), - returnExpr: returnExpr, - returnType: abiReturnType - ) + + let funcLines: [String] + if function.effects.isAsync { + // For async functions, use the continuation-pointer pattern. + // The generated function takes continuationPtr as first param, + // calls the import, attaches .then/.catch on the returned Promise, + // and calls resolve/reject continuations. + thunkBuilder.callAsync(name: jsName, fromObjectExpr: importRootExpr) + funcLines = thunkBuilder.renderAsyncFunction(name: function.abiName(context: nil)) + } else { + let returnExpr = try thunkBuilder.call( + name: jsName, + fromObjectExpr: importRootExpr, + returnType: function.returnType + ) + funcLines = thunkBuilder.renderFunction( + name: function.abiName(context: nil), + returnExpr: returnExpr, + returnType: function.returnType + ) + } if function.from == nil { importObjectBuilder.appendDts( [ @@ -3339,13 +3493,19 @@ extension BridgeJSLink { for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let abiReturnType: BridgeType = method.effects.isAsync ? .jsObject(nil) : method.returnType - let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: abiReturnType) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName(context: context), - returnExpr: returnExpr, - returnType: abiReturnType - ) + + let funcLines: [String] + if method.effects.isAsync { + thunkBuilder.callAsyncMethod(name: method.jsName ?? method.name) + funcLines = thunkBuilder.renderAsyncFunction(name: method.abiName(context: context)) + } else { + let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: method.returnType) + funcLines = thunkBuilder.renderFunction( + name: method.abiName(context: context), + returnExpr: returnExpr, + returnType: method.returnType + ) + } return (funcLines, []) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift index eed78f197..09af0d983 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -1,138 +1,118 @@ #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncReturnVoid") -fileprivate func bjs_asyncReturnVoid_extern() -> Int32 +fileprivate func bjs_asyncReturnVoid_extern(_ continuationPtr: Int32) -> Void #else -fileprivate func bjs_asyncReturnVoid_extern() -> Int32 { +fileprivate func bjs_asyncReturnVoid_extern(_ continuationPtr: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncReturnVoid() -> Int32 { - return bjs_asyncReturnVoid_extern() +@inline(never) fileprivate func bjs_asyncReturnVoid(_ continuationPtr: Int32) -> Void { + return bjs_asyncReturnVoid_extern(continuationPtr) } func _$asyncReturnVoid() async throws(JSException) -> Void { - let ret = bjs_asyncReturnVoid() - if let error = _swift_js_take_exception() { - throw error + _ = try await _bjs_awaitPromise { continuationPtr in + bjs_asyncReturnVoid(continuationPtr) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - _ = try await promise.value } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripInt") -fileprivate func bjs_asyncRoundTripInt_extern(_ v: Int32) -> Int32 +fileprivate func bjs_asyncRoundTripInt_extern(_ continuationPtr: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripInt_extern(_ v: Int32) -> Int32 { +fileprivate func bjs_asyncRoundTripInt_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripInt(_ v: Int32) -> Int32 { - return bjs_asyncRoundTripInt_extern(v) +@inline(never) fileprivate func bjs_asyncRoundTripInt(_ continuationPtr: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripInt_extern(continuationPtr, v) } func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_asyncRoundTripInt(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripInt(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return Int(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripString") -fileprivate func bjs_asyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 +fileprivate func bjs_asyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 { +fileprivate func bjs_asyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripString(_ vBytes: Int32, _ vLength: Int32) -> Int32 { - return bjs_asyncRoundTripString_extern(vBytes, vLength) +@inline(never) fileprivate func bjs_asyncRoundTripString(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_asyncRoundTripString_extern(continuationPtr, vBytes, vLength) } func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { - let ret0 = v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - let ret = bjs_asyncRoundTripString(vBytes, vLength) - return ret + let resolved = try await _bjs_awaitPromise { continuationPtr in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_asyncRoundTripString(continuationPtr, vBytes, vLength) + } } - let ret = ret0 - if let error = _swift_js_take_exception() { - throw error - } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return resolved.string! } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripBool") -fileprivate func bjs_asyncRoundTripBool_extern(_ v: Int32) -> Int32 +fileprivate func bjs_asyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripBool_extern(_ v: Int32) -> Int32 { +fileprivate func bjs_asyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripBool(_ v: Int32) -> Int32 { - return bjs_asyncRoundTripBool_extern(v) +@inline(never) fileprivate func bjs_asyncRoundTripBool(_ continuationPtr: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripBool_extern(continuationPtr, v) } func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_asyncRoundTripBool(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripBool(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return resolved.boolean! } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripDouble") -fileprivate func bjs_asyncRoundTripDouble_extern(_ v: Float64) -> Int32 +fileprivate func bjs_asyncRoundTripDouble_extern(_ continuationPtr: Int32, _ v: Float64) -> Void #else -fileprivate func bjs_asyncRoundTripDouble_extern(_ v: Float64) -> Int32 { +fileprivate func bjs_asyncRoundTripDouble_extern(_ continuationPtr: Int32, _ v: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ v: Float64) -> Int32 { - return bjs_asyncRoundTripDouble_extern(v) +@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ continuationPtr: Int32, _ v: Float64) -> Void { + return bjs_asyncRoundTripDouble_extern(continuationPtr, v) } func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_asyncRoundTripDouble(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripDouble(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return Double(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripJSObject") -fileprivate func bjs_asyncRoundTripJSObject_extern(_ v: Int32) -> Int32 +fileprivate func bjs_asyncRoundTripJSObject_extern(_ continuationPtr: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripJSObject_extern(_ v: Int32) -> Int32 { +fileprivate func bjs_asyncRoundTripJSObject_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ v: Int32) -> Int32 { - return bjs_asyncRoundTripJSObject_extern(v) +@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ continuationPtr: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripJSObject_extern(continuationPtr, v) } func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_asyncRoundTripJSObject(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_asyncRoundTripJSObject(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return resolved.object! } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js index 28943b686..1d321de02 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -30,6 +30,80 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; + function bjs_resolvePromiseContinuation(ptr, value) { + let kind, payload1 = 0, payload2 = 0; + if (value === null) { + kind = 4; + } else if (value === undefined) { + kind = 5; + } else { + const type = typeof value; + switch (type) { + case "boolean": + kind = 0; + payload1 = value ? 1 : 0; + break; + case "number": + kind = 2; + payload2 = value; + break; + case "string": + kind = 1; + payload1 = swift.memory.retain(value); + break; + case "symbol": + kind = 7; + payload1 = swift.memory.retain(value); + break; + case "bigint": + kind = 8; + payload1 = swift.memory.retain(value); + break; + default: + kind = 3; + payload1 = swift.memory.retain(value); + break; + } + } + instance.exports.bjs_resolve_promise_continuation(ptr, kind, payload1, payload2); + } + function bjs_rejectPromiseContinuation(ptr, error) { + let kind, payload1 = 0, payload2 = 0; + if (error === null) { + kind = 4; + } else if (error === undefined) { + kind = 5; + } else { + const type = typeof error; + switch (type) { + case "boolean": + kind = 0; + payload1 = error ? 1 : 0; + break; + case "number": + kind = 2; + payload2 = error; + break; + case "string": + kind = 1; + payload1 = swift.memory.retain(error); + break; + case "symbol": + kind = 7; + payload1 = swift.memory.retain(error); + break; + case "bigint": + kind = 8; + payload1 = swift.memory.retain(error); + break; + default: + kind = 3; + payload1 = swift.memory.retain(error); + break; + } + } + instance.exports.bjs_reject_promise_continuation(ptr, kind, payload1, payload2); + } return { /** @@ -190,59 +264,71 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_closure_unregister"] = function(funcRef) {} const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; - TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid() { + TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(continuationPtr) { try { - let ret = imports.asyncReturnVoid(); - return swift.memory.retain(ret); + const promise = imports.asyncReturnVoid(); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } - TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(v) { + TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(continuationPtr, v) { try { - let ret = imports.asyncRoundTripInt(v); - return swift.memory.retain(ret); + const promise = imports.asyncRoundTripInt(v); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } - TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(vBytes, vCount) { + TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(continuationPtr, vBytes, vCount) { try { const string = decodeString(vBytes, vCount); - let ret = imports.asyncRoundTripString(string); - return swift.memory.retain(ret); + const promise = imports.asyncRoundTripString(string); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } - TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(v) { + TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(continuationPtr, v) { try { - let ret = imports.asyncRoundTripBool(v !== 0); - return swift.memory.retain(ret); + const promise = imports.asyncRoundTripBool(v !== 0); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } - TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(v) { + TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(continuationPtr, v) { try { - let ret = imports.asyncRoundTripDouble(v); - return swift.memory.retain(ret); + const promise = imports.asyncRoundTripDouble(v); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } - TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(v) { + TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(continuationPtr, v) { try { - let ret = imports.asyncRoundTripJSObject(swift.memory.getObject(v)); - return swift.memory.retain(ret); + const promise = imports.asyncRoundTripJSObject(swift.memory.getObject(v)); + promise.then( + (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, + (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } + ); } catch (error) { - setException(error); - return 0 + bjs_rejectPromiseContinuation(continuationPtr, error); } } }, diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 180567ed1..f1d037400 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -2083,3 +2083,91 @@ extension _BridgedAsOptional { Wrapped.bridgeJSStackPushAsOptional(asOptional) } } + +// MARK: Promise Continuation Pattern + +/// A box that stores an `UnsafeContinuation` for a Promise result. +/// +/// Used by the continuation-pointer pattern for async BridgeJS imports. +/// The JS side resolves or rejects the Promise and calls back into Wasm +/// via `bjs_resolve_promise_continuation` / `bjs_reject_promise_continuation`, +/// which resume the continuation stored here. +@_spi(BridgeJS) public final class _BJSPromiseContinuationBox { + let continuation: UnsafeContinuation, Never> + init(continuation: UnsafeContinuation, Never>) { + self.continuation = continuation + } +} + +/// Awaits a JavaScript Promise using the continuation-pointer pattern. +/// +/// This creates a `_BJSPromiseContinuationBox`, passes its pointer (as `Int32`) +/// to the `body` closure which should forward it to a JS extern function. +/// The JS side attaches `.then`/`.catch` handlers and calls back into +/// `bjs_resolve_promise_continuation` or `bjs_reject_promise_continuation` +/// when the Promise settles. +/// +/// - Parameter body: A closure that receives the continuation pointer as `Int32` +/// and should pass it to the appropriate JS extern function. +/// - Returns: The resolved `JSValue` from the Promise. +/// - Throws: `JSException` if the Promise rejects. +@_spi(BridgeJS) public func _bjs_awaitPromise(_ body: (Int32) -> Void) async throws(JSException) -> JSValue { + let result: Result = await withUnsafeContinuation { continuation in + let box = _BJSPromiseContinuationBox(continuation: continuation) + let pointer = Unmanaged.passRetained(box).toOpaque() + body(Int32(bitPattern: UInt32(UInt(bitPattern: pointer)))) + } + return try result.get() +} + +#if arch(wasm32) +/// Wasm export called by JS when a Promise resolves. +/// +/// The JS side encodes the resolved value as `(kind, payload1, payload2)` +/// using the same `RawJSValue` encoding as the rest of JavaScriptKit. +@_expose(wasm, "bjs_resolve_promise_continuation") +@_cdecl("bjs_resolve_promise_continuation") +public func _bjs_resolve_promise_continuation( + _ rawPtr: Int32, + _ kind: Int32, + _ payload1: Int32, + _ payload2: Float64 +) { + let pointer = UnsafeMutableRawPointer(bitPattern: UInt(UInt32(bitPattern: rawPtr)))! + let box = Unmanaged<_BJSPromiseContinuationBox>.fromOpaque(pointer).takeRetainedValue() + guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { + fatalError("Invalid JSValue kind: \(kind)") + } + let rawValue = RawJSValue( + kind: kindEnum, + payload1: JavaScriptPayload1(UInt32(bitPattern: payload1)), + payload2: payload2 + ) + box.continuation.resume(returning: .success(rawValue.jsValue)) +} + +/// Wasm export called by JS when a Promise rejects. +/// +/// The JS side encodes the rejection value as `(kind, payload1, payload2)` +/// using the same `RawJSValue` encoding as the rest of JavaScriptKit. +@_expose(wasm, "bjs_reject_promise_continuation") +@_cdecl("bjs_reject_promise_continuation") +public func _bjs_reject_promise_continuation( + _ rawPtr: Int32, + _ kind: Int32, + _ payload1: Int32, + _ payload2: Float64 +) { + let pointer = UnsafeMutableRawPointer(bitPattern: UInt(UInt32(bitPattern: rawPtr)))! + let box = Unmanaged<_BJSPromiseContinuationBox>.fromOpaque(pointer).takeRetainedValue() + guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { + fatalError("Invalid JSValue kind: \(kind)") + } + let rawValue = RawJSValue( + kind: kindEnum, + payload1: JavaScriptPayload1(UInt32(bitPattern: payload1)), + payload2: payload2 + ) + box.continuation.resume(returning: .failure(JSException(rawValue.jsValue))) +} +#endif diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index a1990ab0c..0ebf6558d 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -11127,141 +11127,119 @@ func _$jsRoundTripFeatureFlag(_ flag: FeatureFlag) throws(JSException) -> Featur #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_runAsyncWorks") -fileprivate func bjs_runAsyncWorks_extern() -> Int32 +fileprivate func bjs_runAsyncWorks_extern(_ continuationPtr: Int32) -> Void #else -fileprivate func bjs_runAsyncWorks_extern() -> Int32 { +fileprivate func bjs_runAsyncWorks_extern(_ continuationPtr: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_runAsyncWorks() -> Int32 { - return bjs_runAsyncWorks_extern() +@inline(never) fileprivate func bjs_runAsyncWorks(_ continuationPtr: Int32) -> Void { + return bjs_runAsyncWorks_extern(continuationPtr) } func _$runAsyncWorks() async throws(JSException) -> Void { - let ret = bjs_runAsyncWorks() - if let error = _swift_js_take_exception() { - throw error + _ = try await _bjs_awaitPromise { continuationPtr in + bjs_runAsyncWorks(continuationPtr) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - _ = try await promise.value } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripVoid") -fileprivate func bjs_jsAsyncRoundTripVoid_extern() -> Int32 +fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ continuationPtr: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripVoid_extern() -> Int32 { +fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ continuationPtr: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripVoid() -> Int32 { - return bjs_jsAsyncRoundTripVoid_extern() +@inline(never) fileprivate func bjs_jsAsyncRoundTripVoid(_ continuationPtr: Int32) -> Void { + return bjs_jsAsyncRoundTripVoid_extern(continuationPtr) } func _$jsAsyncRoundTripVoid() async throws(JSException) -> Void { - let ret = bjs_jsAsyncRoundTripVoid() - if let error = _swift_js_take_exception() { - throw error + _ = try await _bjs_awaitPromise { continuationPtr in + bjs_jsAsyncRoundTripVoid(continuationPtr) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - _ = try await promise.value } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripNumber") -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ v: Float64) -> Int32 +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ continuationPtr: Int32, _ v: Float64) -> Void #else -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ v: Float64) -> Int32 { +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ continuationPtr: Int32, _ v: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripNumber(_ v: Float64) -> Int32 { - return bjs_jsAsyncRoundTripNumber_extern(v) +@inline(never) fileprivate func bjs_jsAsyncRoundTripNumber(_ continuationPtr: Int32, _ v: Float64) -> Void { + return bjs_jsAsyncRoundTripNumber_extern(continuationPtr, v) } func _$jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_jsAsyncRoundTripNumber(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripNumber(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return Double(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBool") -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ v: Int32) -> Int32 +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ v: Int32) -> Int32 { +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripBool(_ v: Int32) -> Int32 { - return bjs_jsAsyncRoundTripBool_extern(v) +@inline(never) fileprivate func bjs_jsAsyncRoundTripBool(_ continuationPtr: Int32, _ v: Int32) -> Void { + return bjs_jsAsyncRoundTripBool_extern(continuationPtr, v) } func _$jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let vValue = v.bridgeJSLowerParameter() - let ret = bjs_jsAsyncRoundTripBool(vValue) - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + let vValue = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripBool(continuationPtr, vValue) } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return resolved.boolean! } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripString") -fileprivate func bjs_jsAsyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 +fileprivate func bjs_jsAsyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripString_extern(_ vBytes: Int32, _ vLength: Int32) -> Int32 { +fileprivate func bjs_jsAsyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripString(_ vBytes: Int32, _ vLength: Int32) -> Int32 { - return bjs_jsAsyncRoundTripString_extern(vBytes, vLength) +@inline(never) fileprivate func bjs_jsAsyncRoundTripString(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_jsAsyncRoundTripString_extern(continuationPtr, vBytes, vLength) } func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { - let ret0 = v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - let ret = bjs_jsAsyncRoundTripString(vBytes, vLength) - return ret - } - let ret = ret0 - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_jsAsyncRoundTripString(continuationPtr, vBytes, vLength) + } } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return resolved.string! } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") -fileprivate func bjs_fetchWeatherData_extern(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 +fileprivate func bjs_fetchWeatherData_extern(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void #else -fileprivate func bjs_fetchWeatherData_extern(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 { +fileprivate func bjs_fetchWeatherData_extern(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_fetchWeatherData(_ cityBytes: Int32, _ cityLength: Int32) -> Int32 { - return bjs_fetchWeatherData_extern(cityBytes, cityLength) +@inline(never) fileprivate func bjs_fetchWeatherData(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + return bjs_fetchWeatherData_extern(continuationPtr, cityBytes, cityLength) } func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { - let ret0 = city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in - let ret = bjs_fetchWeatherData(cityBytes, cityLength) - return ret - } - let ret = ret0 - if let error = _swift_js_take_exception() { - throw error + let resolved = try await _bjs_awaitPromise { continuationPtr in + city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in + bjs_fetchWeatherData(continuationPtr, cityBytes, cityLength) + } } - let promise = JSPromise(unsafelyWrapping: JSObject(id: UInt32(bitPattern: ret))) - let resolved = try await promise.value return WeatherData(unsafelyWrapping: resolved.object!) } From 786461744596d8a9e7c5de89a42f78c9b2c77b23 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 12:31:12 +0000 Subject: [PATCH 05/10] Clean up `BridgeJSLink` --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 23 +- .../Sources/BridgeJSCore/ImportTS.swift | 28 +-- .../Sources/BridgeJSLink/BridgeJSLink.swift | 144 +++--------- .../BridgeJSCodegenTests/AsyncImport.swift | 145 +++++++++--- .../BridgeJSLinkTests/AsyncImport.js | 218 +++++++++++------- .../JavaScriptKit/BridgeJSIntrinsics.swift | 115 +++------ .../Generated/BridgeJS.swift | 145 +++++++++--- 7 files changed, 460 insertions(+), 358 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index 142050f56..a274a4a6f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -188,7 +188,28 @@ public struct ClosureCodegen { let collector = ClosureSignatureCollectorVisitor() var walker = BridgeTypeWalker(visitor: collector) walker.walk(skeleton) - let closureSignatures = walker.visitor.signatures + var closureSignatures = walker.visitor.signatures + + // When any async import exists, inject a (JSValue) -> Void closure signature + // so the closure infrastructure generates the make/invoke exports needed by + // _bjs_awaitPromise's resolve/reject callbacks. + if let imported = skeleton.imported { + let hasAsyncImport = imported.children.contains { file in + file.functions.contains(where: { $0.effects.isAsync }) + || file.types.contains(where: { type in + type.methods.contains(where: { $0.effects.isAsync }) + }) + } + if hasAsyncImport { + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + } + } guard !closureSignatures.isEmpty else { return nil } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 3e4826904..2688a9a83 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -212,13 +212,13 @@ public struct ImportTS { } } - /// Prepends a `continuationPtr: Int32` parameter to the ABI parameter list. + /// Prepends `resolveRef: Int32, rejectRef: Int32` parameters to the ABI parameter list. /// - /// Used for async imports where the JS side needs the continuation pointer - /// to resolve/reject the Promise. - func prependContinuationPtr() { - abiParameterSignatures.insert(("continuationPtr", .i32), at: 0) - abiParameterForwardings.insert("continuationPtr", at: 0) + /// Used for async imports where the JS side receives closure-backed + /// resolve/reject callbacks as object references. + func prependClosureCallbackParams() { + abiParameterSignatures.insert(contentsOf: [("resolveRef", .i32), ("rejectRef", .i32)], at: 0) + abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0) } func call(skipExceptionCheck: Bool = false) throws { @@ -289,10 +289,8 @@ public struct ImportTS { } func liftAsyncReturnValue(originalReturnType: BridgeType) { - // For async imports, we use the continuation-pointer pattern. - // The extern function takes a leading `continuationPtr: Int32` and returns void. - // The JS side attaches .then/.catch handlers and calls back into Wasm - // via bjs_resolve_promise_continuation / bjs_reject_promise_continuation. + // For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32` + // and returns void. The JS side calls the resolve/reject closures when the Promise settles. abiReturnType = nil // Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise @@ -300,9 +298,11 @@ public struct ImportTS { body = CodeFragmentPrinter() if originalReturnType == .void { - body.write("_ = try await _bjs_awaitPromise { continuationPtr in") + body.write("_ = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in") } else { - body.write("let resolved = try await _bjs_awaitPromise { continuationPtr in") + body.write( + "let resolved = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in" + ) } body.indent { body.write(lines: innerBody.lines) @@ -431,7 +431,7 @@ public struct ImportTS { returnType: abiReturnType ) if function.effects.isAsync { - builder.prependContinuationPtr() + builder.prependClosureCallbackParams() } for param in function.parameters { try builder.lowerParameter(param: param) @@ -466,7 +466,7 @@ public struct ImportTS { returnType: abiReturnType ) if method.effects.isAsync { - builder.prependContinuationPtr() + builder.prependClosureCallbackParams() } try builder.lowerParameter(param: selfParameter) for param in method.parameters { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index a8d7e5e43..70bd6420b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -135,7 +135,6 @@ public struct BridgeJSLink { var importObjectBuilders: [ImportObjectBuilder] = [] var enumStaticAssignments: [String] = [] var needsImportsObject: Bool = false - var hasAsyncImports: Bool = false } private func collectLinkData() throws -> LinkData { @@ -238,19 +237,12 @@ public struct BridgeJSLink { if function.from == nil { data.needsImportsObject = true } - if function.effects.isAsync { - data.hasAsyncImports = true - } try renderImportedFunction(importObjectBuilder: importObjectBuilder, function: function) } for type in fileSkeleton.types { if type.constructor != nil, type.from == nil { data.needsImportsObject = true } - // Check for async methods in imported types - for method in type.methods where method.effects.isAsync { - data.hasAsyncImports = true - } try renderImportedType(importObjectBuilder: importObjectBuilder, type: type) } } @@ -322,85 +314,6 @@ public struct BridgeJSLink { ] } - /// Generates helper functions for the continuation-pointer pattern used by async imports. - /// - /// These encode a JS value as `(kind, payload1, payload2)` matching the `RawJSValue` - /// encoding from `_CJavaScriptKit.h`, then call the appropriate Wasm export to resume - /// the Swift continuation. - private func generatePromiseContinuationHelpers() -> [String] { - let printer = CodeFragmentPrinter() - // Helper to encode a JS value into (kind, payload1, payload2) and call the resolve export - printer.write("function bjs_resolvePromiseContinuation(ptr, value) {") - printer.indent { - printer.write(lines: generateJSValueEncoding(variableName: "value")) - printer.write( - "\(JSGlueVariableScope.reservedInstance).exports.bjs_resolve_promise_continuation(ptr, kind, payload1, payload2);" - ) - } - printer.write("}") - // Helper to encode a JS value into (kind, payload1, payload2) and call the reject export - printer.write("function bjs_rejectPromiseContinuation(ptr, error) {") - printer.indent { - printer.write(lines: generateJSValueEncoding(variableName: "error")) - printer.write( - "\(JSGlueVariableScope.reservedInstance).exports.bjs_reject_promise_continuation(ptr, kind, payload1, payload2);" - ) - } - printer.write("}") - return printer.lines - } - - /// Generates JS code that encodes a variable into `(kind, payload1, payload2)`. - /// - /// The encoding matches `JavaScriptValueKind` from `_CJavaScriptKit.h`: - /// - Boolean(0): payload1 = 1 or 0 - /// - String(1): payload1 = retained object ID - /// - Number(2): payload2 = the number - /// - Object(3): payload1 = retained object ID - /// - Null(4): no payload - /// - Undefined(5): no payload - /// - Symbol(7): payload1 = retained object ID - /// - BigInt(8): payload1 = retained object ID - private func generateJSValueEncoding(variableName: String) -> [String] { - let s = JSGlueVariableScope.reservedSwift - return [ - "let kind, payload1 = 0, payload2 = 0;", - "if (\(variableName) === null) {", - " kind = 4;", - "} else if (\(variableName) === undefined) {", - " kind = 5;", - "} else {", - " const type = typeof \(variableName);", - " switch (type) {", - " case \"boolean\":", - " kind = 0;", - " payload1 = \(variableName) ? 1 : 0;", - " break;", - " case \"number\":", - " kind = 2;", - " payload2 = \(variableName);", - " break;", - " case \"string\":", - " kind = 1;", - " payload1 = \(s).memory.retain(\(variableName));", - " break;", - " case \"symbol\":", - " kind = 7;", - " payload1 = \(s).memory.retain(\(variableName));", - " break;", - " case \"bigint\":", - " kind = 8;", - " payload1 = \(s).memory.retain(\(variableName));", - " break;", - " default:", - " kind = 3;", - " payload1 = \(s).memory.retain(\(variableName));", - " break;", - " }", - "}", - ] - } - private func generateAddImports(needsImportsObject: Bool) throws -> CodeFragmentPrinter { let printer = CodeFragmentPrinter() let allStructs = skeletons.compactMap { $0.exported?.structs }.flatMap { $0 } @@ -726,7 +639,26 @@ public struct BridgeJSLink { let collector = ClosureSignatureCollectorVisitor() var walker = BridgeTypeWalker(visitor: collector) walker.walk(unified) - let closureSignatures = walker.visitor.signatures + var closureSignatures = walker.visitor.signatures + + // Inject (JSValue) -> Void closure signature when async imports exist + if let imported = unified.imported { + let hasAsyncImport = imported.children.contains { file in + file.functions.contains(where: { $0.effects.isAsync }) + || file.types.contains(where: { type in + type.methods.contains(where: { $0.effects.isAsync }) + }) + } + if hasAsyncImport { + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: moduleName + ) + ) + } + } guard !closureSignatures.isEmpty else { continue } @@ -1057,11 +989,6 @@ public struct BridgeJSLink { try printer.indent { printer.write(lines: generateVariableDeclarations()) - // Generate Promise continuation helpers when async imports exist - if data.hasAsyncImports { - printer.write(lines: generatePromiseContinuationHelpers()) - } - let bodyPrinter = CodeFragmentPrinter() let allStructs = exportedSkeletons.flatMap { $0.structs } for structDef in allStructs { @@ -2327,36 +2254,34 @@ extension BridgeJSLink { /// Generates the call expression for an async import. /// /// Instead of lowering the return value, this assigns the result to `promise` - /// and attaches `.then`/`.catch` handlers that call the resolve/reject continuations. + /// and passes the resolve/reject closure refs to `.then`. func callAsync(name: String, fromObjectExpr: String) { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" body.write("const promise = \(callExpr);") - body.write("promise.then(") - body.indent { - body.write("(value) => { bjs_resolvePromiseContinuation(continuationPtr, value); },") - body.write("(error) => { bjs_rejectPromiseContinuation(continuationPtr, error); }") - } - body.write(");") + body.write("promise.then(resolve, reject);") } - /// Renders an async import function with continuation-pointer pattern. + /// Renders an async import function with resolve/reject closure refs. /// - /// The generated function takes `continuationPtr` as the first parameter, + /// The generated function takes `resolveRef` and `rejectRef` as the first parameters, /// wraps the import call in try/catch, attaches Promise handlers, and - /// calls reject continuation on synchronous errors. + /// calls reject on synchronous errors. func renderAsyncFunction(name: String?) -> [String] { let printer = CodeFragmentPrinter() - let allParams = ["continuationPtr"] + parameterNames + let allParams = ["resolveRef", "rejectRef"] + parameterNames printer.write("function\(name.map { " \($0)" } ?? "")(\(allParams.joined(separator: ", "))) {") printer.indent { + let s = JSGlueVariableScope.reservedSwift + printer.write("const resolve = \(s).memory.getObject(resolveRef);") + printer.write("const reject = \(s).memory.getObject(rejectRef);") printer.write("try {") printer.indent { printer.write(contentsOf: body) } printer.write("} catch (error) {") printer.indent { - printer.write("bjs_rejectPromiseContinuation(continuationPtr, error);") + printer.write("reject(error);") } printer.write("}") } @@ -2417,18 +2342,13 @@ extension BridgeJSLink { ) } - /// Generates an async method call with continuation-pointer pattern. + /// Generates an async method call with resolve/reject closure refs. func callAsyncMethod(name: String) { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" body.write("const promise = \(callExpr);") - body.write("promise.then(") - body.indent { - body.write("(value) => { bjs_resolvePromiseContinuation(continuationPtr, value); },") - body.write("(error) => { bjs_rejectPromiseContinuation(continuationPtr, error); }") - } - body.write(");") + body.write("promise.then(resolve, reject);") } func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws -> String? { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift index 09af0d983..5985587f7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -1,57 +1,124 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule7JSValueV_y") +fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule7JSValueV_y") +fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule7JSValueV_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule7JSValueV_y") +public func _invoke_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncReturnVoid") -fileprivate func bjs_asyncReturnVoid_extern(_ continuationPtr: Int32) -> Void +fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void #else -fileprivate func bjs_asyncReturnVoid_extern(_ continuationPtr: Int32) -> Void { +fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncReturnVoid(_ continuationPtr: Int32) -> Void { - return bjs_asyncReturnVoid_extern(continuationPtr) +@inline(never) fileprivate func bjs_asyncReturnVoid(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_asyncReturnVoid_extern(resolveRef, rejectRef) } func _$asyncReturnVoid() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise { continuationPtr in - bjs_asyncReturnVoid(continuationPtr) + _ = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in + bjs_asyncReturnVoid(resolveRef, rejectRef) } } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripInt") -fileprivate func bjs_asyncRoundTripInt_extern(_ continuationPtr: Int32, _ v: Int32) -> Void +fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripInt_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { +fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripInt(_ continuationPtr: Int32, _ v: Int32) -> Void { - return bjs_asyncRoundTripInt_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_asyncRoundTripInt(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripInt_extern(resolveRef, rejectRef, v) } func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_asyncRoundTripInt(continuationPtr, vValue) + bjs_asyncRoundTripInt(resolveRef, rejectRef, vValue) } return Int(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripString") -fileprivate func bjs_asyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { +fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripString(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - return bjs_asyncRoundTripString_extern(continuationPtr, vBytes, vLength) +@inline(never) fileprivate func bjs_asyncRoundTripString(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_asyncRoundTripString_extern(resolveRef, rejectRef, vBytes, vLength) } func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - bjs_asyncRoundTripString(continuationPtr, vBytes, vLength) + bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) } } return resolved.string! @@ -59,60 +126,66 @@ func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripBool") -fileprivate func bjs_asyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void +fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { +fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripBool(_ continuationPtr: Int32, _ v: Int32) -> Void { - return bjs_asyncRoundTripBool_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_asyncRoundTripBool(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripBool_extern(resolveRef, rejectRef, v) } func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_asyncRoundTripBool(continuationPtr, vValue) + bjs_asyncRoundTripBool(resolveRef, rejectRef, vValue) } return resolved.boolean! } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripDouble") -fileprivate func bjs_asyncRoundTripDouble_extern(_ continuationPtr: Int32, _ v: Float64) -> Void +fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void #else -fileprivate func bjs_asyncRoundTripDouble_extern(_ continuationPtr: Int32, _ v: Float64) -> Void { +fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ continuationPtr: Int32, _ v: Float64) -> Void { - return bjs_asyncRoundTripDouble_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_asyncRoundTripDouble(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_asyncRoundTripDouble_extern(resolveRef, rejectRef, v) } func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_asyncRoundTripDouble(continuationPtr, vValue) + bjs_asyncRoundTripDouble(resolveRef, rejectRef, vValue) } return Double(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncRoundTripJSObject") -fileprivate func bjs_asyncRoundTripJSObject_extern(_ continuationPtr: Int32, _ v: Int32) -> Void +fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_asyncRoundTripJSObject_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { +fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ continuationPtr: Int32, _ v: Int32) -> Void { - return bjs_asyncRoundTripJSObject_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_asyncRoundTripJSObject(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_asyncRoundTripJSObject_extern(resolveRef, rejectRef, v) } func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_asyncRoundTripJSObject(continuationPtr, vValue) + bjs_asyncRoundTripJSObject(resolveRef, rejectRef, vValue) } return resolved.object! } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js index 1d321de02..0feffd08e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -30,81 +30,121 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - function bjs_resolvePromiseContinuation(ptr, value) { - let kind, payload1 = 0, payload2 = 0; + function __bjs_jsValueLower(value) { + let kind; + let payload1; + let payload2; if (value === null) { kind = 4; - } else if (value === undefined) { - kind = 5; + payload1 = 0; + payload2 = 0; } else { - const type = typeof value; - switch (type) { + switch (typeof value) { case "boolean": kind = 0; payload1 = value ? 1 : 0; + payload2 = 0; break; case "number": kind = 2; + payload1 = 0; payload2 = value; break; case "string": kind = 1; payload1 = swift.memory.retain(value); + payload2 = 0; break; - case "symbol": - kind = 7; - payload1 = swift.memory.retain(value); + case "undefined": + kind = 5; + payload1 = 0; + payload2 = 0; break; - case "bigint": - kind = 8; + case "object": + kind = 3; payload1 = swift.memory.retain(value); + payload2 = 0; break; - default: + case "function": kind = 3; payload1 = swift.memory.retain(value); - break; - } - } - instance.exports.bjs_resolve_promise_continuation(ptr, kind, payload1, payload2); - } - function bjs_rejectPromiseContinuation(ptr, error) { - let kind, payload1 = 0, payload2 = 0; - if (error === null) { - kind = 4; - } else if (error === undefined) { - kind = 5; - } else { - const type = typeof error; - switch (type) { - case "boolean": - kind = 0; - payload1 = error ? 1 : 0; - break; - case "number": - kind = 2; - payload2 = error; - break; - case "string": - kind = 1; - payload1 = swift.memory.retain(error); + payload2 = 0; break; case "symbol": kind = 7; - payload1 = swift.memory.retain(error); + payload1 = swift.memory.retain(value); + payload2 = 0; break; case "bigint": kind = 8; - payload1 = swift.memory.retain(error); + payload1 = swift.memory.retain(value); + payload2 = 0; break; default: - kind = 3; - payload1 = swift.memory.retain(error); - break; + throw new TypeError("Unsupported JSValue type"); } } - instance.exports.bjs_reject_promise_continuation(ptr, kind, payload1, payload2); + return [kind, payload1, payload2]; + } + function __bjs_jsValueLift(kind, payload1, payload2) { + let jsValue; + switch (kind) { + case 0: + jsValue = payload1 !== 0; + break; + case 1: + jsValue = swift.memory.getObject(payload1); + break; + case 2: + jsValue = payload2; + break; + case 3: + jsValue = swift.memory.getObject(payload1); + break; + case 4: + jsValue = null; + break; + case 5: + jsValue = undefined; + break; + case 7: + jsValue = swift.memory.getObject(payload1); + break; + case 8: + jsValue = swift.memory.getObject(payload1); + break; + default: + throw new TypeError("Unsupported JSValue kind " + kind); + } + return jsValue; } + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + return { /** * @param {WebAssembly.Imports} importObject @@ -263,72 +303,92 @@ export async function createInstantiator(options, swift) { return pointer || 0; } bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModule7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { + try { + const callback = swift.memory.getObject(callbackId); + const jsValue = __bjs_jsValueLift(param0Kind, param0Payload1, param0Payload2); + callback(jsValue); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule7JSValueV_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule7JSValueV_y = function(param0) { + const [param0Kind, param0Payload1, param0Payload2] = __bjs_jsValueLower(param0); + instance.exports.invoke_swift_closure_TestModule_10TestModule7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule7JSValueV_y); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; - TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(continuationPtr) { + TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(resolveRef, rejectRef) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const promise = imports.asyncReturnVoid(); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } - TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(continuationPtr, v) { + TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const promise = imports.asyncRoundTripInt(v); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } - TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(continuationPtr, vBytes, vCount) { + TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vCount) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const string = decodeString(vBytes, vCount); const promise = imports.asyncRoundTripString(string); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } - TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(continuationPtr, v) { + TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const promise = imports.asyncRoundTripBool(v !== 0); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } - TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(continuationPtr, v) { + TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const promise = imports.asyncRoundTripDouble(v); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } - TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(continuationPtr, v) { + TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); try { const promise = imports.asyncRoundTripJSObject(swift.memory.getObject(v)); - promise.then( - (value) => { bjs_resolvePromiseContinuation(continuationPtr, value); }, - (error) => { bjs_rejectPromiseContinuation(continuationPtr, error); } - ); + promise.then(resolve, reject); } catch (error) { - bjs_rejectPromiseContinuation(continuationPtr, error); + reject(error); } } }, diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index f1d037400..3edc1109b 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -2084,90 +2084,45 @@ extension _BridgedAsOptional { } } -// MARK: Promise Continuation Pattern +// MARK: Async Promise Awaiting -/// A box that stores an `UnsafeContinuation` for a Promise result. +/// Awaits a JavaScript Promise using `JSTypedClosure<(JSValue) -> Void>` callbacks. /// -/// Used by the continuation-pointer pattern for async BridgeJS imports. -/// The JS side resolves or rejects the Promise and calls back into Wasm -/// via `bjs_resolve_promise_continuation` / `bjs_reject_promise_continuation`, -/// which resume the continuation stored here. -@_spi(BridgeJS) public final class _BJSPromiseContinuationBox { - let continuation: UnsafeContinuation, Never> - init(continuation: UnsafeContinuation, Never>) { - self.continuation = continuation - } -} - -/// Awaits a JavaScript Promise using the continuation-pointer pattern. -/// -/// This creates a `_BJSPromiseContinuationBox`, passes its pointer (as `Int32`) -/// to the `body` closure which should forward it to a JS extern function. -/// The JS side attaches `.then`/`.catch` handlers and calls back into -/// `bjs_resolve_promise_continuation` or `bjs_reject_promise_continuation` -/// when the Promise settles. +/// The `makeClosure` factory is dependency-injected because this library cannot +/// reference per-module generated `make_swift_closure_*` externs. The generated +/// code passes `{ JSTypedClosure($0) }` which uses the per-module convenience init. /// -/// - Parameter body: A closure that receives the continuation pointer as `Int32` -/// and should pass it to the appropriate JS extern function. +/// - Parameters: +/// - makeClosure: A factory that wraps a `(JSValue) -> Void` Swift closure +/// into a `JSTypedClosure`, creating the corresponding JS function. +/// - body: A closure that receives the resolve and reject JS object refs +/// (as `Int32`) and should pass them to the appropriate JS extern function. /// - Returns: The resolved `JSValue` from the Promise. /// - Throws: `JSException` if the Promise rejects. -@_spi(BridgeJS) public func _bjs_awaitPromise(_ body: (Int32) -> Void) async throws(JSException) -> JSValue { - let result: Result = await withUnsafeContinuation { continuation in - let box = _BJSPromiseContinuationBox(continuation: continuation) - let pointer = Unmanaged.passRetained(box).toOpaque() - body(Int32(bitPattern: UInt32(UInt(bitPattern: pointer)))) +@_spi(BridgeJS) public func _bjs_awaitPromise( + makeClosure: (@escaping (JSValue) -> Void) -> JSTypedClosure<(JSValue) -> Void>, + _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void +) async throws(JSException) -> JSValue { + // Wrapper to send JSValue through the continuation. + // Safe in WebAssembly's single-threaded environment. + struct Wrapper: @unchecked Sendable { + let result: Result + } + var resolveClosure: JSTypedClosure<(JSValue) -> Void>? + var rejectClosure: JSTypedClosure<(JSValue) -> Void>? + let wrapper: Wrapper = await withUnsafeContinuation { continuation in + resolveClosure = makeClosure { value in + continuation.resume(returning: Wrapper(result: .success(value))) + } + rejectClosure = makeClosure { value in + continuation.resume(returning: Wrapper(result: .failure(JSException(value)))) + } + body( + Int32(bitPattern: resolveClosure!.jsObject.id), + Int32(bitPattern: rejectClosure!.jsObject.id) + ) } - return try result.get() + resolveClosure?.release() + rejectClosure?.release() + return try wrapper.result.get() } - -#if arch(wasm32) -/// Wasm export called by JS when a Promise resolves. -/// -/// The JS side encodes the resolved value as `(kind, payload1, payload2)` -/// using the same `RawJSValue` encoding as the rest of JavaScriptKit. -@_expose(wasm, "bjs_resolve_promise_continuation") -@_cdecl("bjs_resolve_promise_continuation") -public func _bjs_resolve_promise_continuation( - _ rawPtr: Int32, - _ kind: Int32, - _ payload1: Int32, - _ payload2: Float64 -) { - let pointer = UnsafeMutableRawPointer(bitPattern: UInt(UInt32(bitPattern: rawPtr)))! - let box = Unmanaged<_BJSPromiseContinuationBox>.fromOpaque(pointer).takeRetainedValue() - guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { - fatalError("Invalid JSValue kind: \(kind)") - } - let rawValue = RawJSValue( - kind: kindEnum, - payload1: JavaScriptPayload1(UInt32(bitPattern: payload1)), - payload2: payload2 - ) - box.continuation.resume(returning: .success(rawValue.jsValue)) -} - -/// Wasm export called by JS when a Promise rejects. -/// -/// The JS side encodes the rejection value as `(kind, payload1, payload2)` -/// using the same `RawJSValue` encoding as the rest of JavaScriptKit. -@_expose(wasm, "bjs_reject_promise_continuation") -@_cdecl("bjs_reject_promise_continuation") -public func _bjs_reject_promise_continuation( - _ rawPtr: Int32, - _ kind: Int32, - _ payload1: Int32, - _ payload2: Float64 -) { - let pointer = UnsafeMutableRawPointer(bitPattern: UInt(UInt32(bitPattern: rawPtr)))! - let box = Unmanaged<_BJSPromiseContinuationBox>.fromOpaque(pointer).takeRetainedValue() - guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { - fatalError("Invalid JSValue kind: \(kind)") - } - let rawValue = RawJSValue( - kind: kindEnum, - payload1: JavaScriptPayload1(UInt32(bitPattern: payload1)), - payload2: payload2 - ) - box.continuation.resume(returning: .failure(JSException(rawValue.jsValue))) -} -#endif diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 0ebf6558d..26615d8a0 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -391,6 +391,67 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7Gr #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTests7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests8JSObjectC_8JSObjectC") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests8JSObjectC_8JSObjectC_extern(_ callback: Int32, _ param0: Int32) -> Int32 @@ -11127,96 +11188,106 @@ func _$jsRoundTripFeatureFlag(_ flag: FeatureFlag) throws(JSException) -> Featur #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_runAsyncWorks") -fileprivate func bjs_runAsyncWorks_extern(_ continuationPtr: Int32) -> Void +fileprivate func bjs_runAsyncWorks_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void #else -fileprivate func bjs_runAsyncWorks_extern(_ continuationPtr: Int32) -> Void { +fileprivate func bjs_runAsyncWorks_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_runAsyncWorks(_ continuationPtr: Int32) -> Void { - return bjs_runAsyncWorks_extern(continuationPtr) +@inline(never) fileprivate func bjs_runAsyncWorks(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_runAsyncWorks_extern(resolveRef, rejectRef) } func _$runAsyncWorks() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise { continuationPtr in - bjs_runAsyncWorks(continuationPtr) + _ = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in + bjs_runAsyncWorks(resolveRef, rejectRef) } } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripVoid") -fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ continuationPtr: Int32) -> Void +fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ continuationPtr: Int32) -> Void { +fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripVoid(_ continuationPtr: Int32) -> Void { - return bjs_jsAsyncRoundTripVoid_extern(continuationPtr) +@inline(never) fileprivate func bjs_jsAsyncRoundTripVoid(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_jsAsyncRoundTripVoid_extern(resolveRef, rejectRef) } func _$jsAsyncRoundTripVoid() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise { continuationPtr in - bjs_jsAsyncRoundTripVoid(continuationPtr) + _ = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in + bjs_jsAsyncRoundTripVoid(resolveRef, rejectRef) } } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripNumber") -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ continuationPtr: Int32, _ v: Float64) -> Void +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void #else -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ continuationPtr: Int32, _ v: Float64) -> Void { +fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripNumber(_ continuationPtr: Int32, _ v: Float64) -> Void { - return bjs_jsAsyncRoundTripNumber_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_jsAsyncRoundTripNumber(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_jsAsyncRoundTripNumber_extern(resolveRef, rejectRef, v) } func _$jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripNumber(continuationPtr, vValue) + bjs_jsAsyncRoundTripNumber(resolveRef, rejectRef, vValue) } return Double(resolved.number!) } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBool") -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ continuationPtr: Int32, _ v: Int32) -> Void { +fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripBool(_ continuationPtr: Int32, _ v: Int32) -> Void { - return bjs_jsAsyncRoundTripBool_extern(continuationPtr, v) +@inline(never) fileprivate func bjs_jsAsyncRoundTripBool(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_jsAsyncRoundTripBool_extern(resolveRef, rejectRef, v) } func _$jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripBool(continuationPtr, vValue) + bjs_jsAsyncRoundTripBool(resolveRef, rejectRef, vValue) } return resolved.boolean! } #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripString") -fileprivate func bjs_jsAsyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +fileprivate func bjs_jsAsyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void #else -fileprivate func bjs_jsAsyncRoundTripString_extern(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { +fileprivate func bjs_jsAsyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripString(_ continuationPtr: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - return bjs_jsAsyncRoundTripString_extern(continuationPtr, vBytes, vLength) +@inline(never) fileprivate func bjs_jsAsyncRoundTripString(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_jsAsyncRoundTripString_extern(resolveRef, rejectRef, vBytes, vLength) } func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - bjs_jsAsyncRoundTripString(continuationPtr, vBytes, vLength) + bjs_jsAsyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) } } return resolved.string! @@ -11224,20 +11295,22 @@ func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") -fileprivate func bjs_fetchWeatherData_extern(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void #else -fileprivate func bjs_fetchWeatherData_extern(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func bjs_fetchWeatherData(_ continuationPtr: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { - return bjs_fetchWeatherData_extern(continuationPtr, cityBytes, cityLength) +@inline(never) fileprivate func bjs_fetchWeatherData(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + return bjs_fetchWeatherData_extern(resolveRef, rejectRef, cityBytes, cityLength) } func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { - let resolved = try await _bjs_awaitPromise { continuationPtr in + let resolved = try await _bjs_awaitPromise(makeClosure: { + JSTypedClosure($0) + }) { resolveRef, rejectRef in city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in - bjs_fetchWeatherData(continuationPtr, cityBytes, cityLength) + bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) } } return WeatherData(unsafelyWrapping: resolved.object!) From 0e84003f6ff2d61db84f7c8f948f97e3d4219f61 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 13:56:45 +0000 Subject: [PATCH 06/10] Fix missing `import _Concurrency` --- Sources/JavaScriptKit/BridgeJSIntrinsics.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 3edc1109b..5a5b2643c 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -4,6 +4,7 @@ /// by the BridgeJS system. import _CJavaScriptKit +import _Concurrency #if !arch(wasm32) @usableFromInline func _onlyAvailableOnWasm() -> Never { From 3044b3af10f64ab2c9763dec8d35f36e05f2c1a7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 14:05:59 +0000 Subject: [PATCH 07/10] Fix formatting --- Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift | 4 +++- Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 2688a9a83..833355d3d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -298,7 +298,9 @@ public struct ImportTS { body = CodeFragmentPrinter() if originalReturnType == .void { - body.write("_ = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in") + body.write( + "_ = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in" + ) } else { body.write( "let resolved = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in" diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 70bd6420b..4d81f8a46 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -3419,7 +3419,10 @@ extension BridgeJSLink { thunkBuilder.callAsyncMethod(name: method.jsName ?? method.name) funcLines = thunkBuilder.renderAsyncFunction(name: method.abiName(context: context)) } else { - let returnExpr = try thunkBuilder.callMethod(name: method.jsName ?? method.name, returnType: method.returnType) + let returnExpr = try thunkBuilder.callMethod( + name: method.jsName ?? method.name, + returnType: method.returnType + ) funcLines = thunkBuilder.renderFunction( name: method.abiName(context: context), returnExpr: returnExpr, From 8d4472eab6cd756be42875d508fed0f492f58989 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 14:37:36 +0000 Subject: [PATCH 08/10] Use `JSTypedClosure` without wrapping the result value --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 76 +++- .../Sources/BridgeJSCore/ImportTS.swift | 36 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 71 ++- .../BridgeJSCodegenTests/AsyncImport.swift | 412 +++++++++++++++++- .../BridgeJSLinkTests/AsyncImport.js | 123 ++++++ .../JavaScriptKit/BridgeJSIntrinsics.swift | 79 +++- .../Generated/BridgeJS.swift | 289 +++++++++++- 7 files changed, 982 insertions(+), 104 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index a274a4a6f..ecd354b7f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -190,24 +190,68 @@ public struct ClosureCodegen { walker.walk(skeleton) var closureSignatures = walker.visitor.signatures - // When any async import exists, inject a (JSValue) -> Void closure signature - // so the closure infrastructure generates the make/invoke exports needed by - // _bjs_awaitPromise's resolve/reject callbacks. + // When async imports exist, inject closure signatures for the typed resolve + // and reject callbacks used by _bjs_awaitPromise. + // - Reject always uses (JSValue) -> Void + // - Resolve uses a typed closure matching the return type (or () -> Void for void) if let imported = skeleton.imported { - let hasAsyncImport = imported.children.contains { file in - file.functions.contains(where: { $0.effects.isAsync }) - || file.types.contains(where: { type in - type.methods.contains(where: { $0.effects.isAsync }) - }) - } - if hasAsyncImport { - closureSignatures.insert( - ClosureSignature( - parameters: [.jsValue], - returnType: .void, - moduleName: skeleton.moduleName + for file in imported.children { + for function in file.functions where function.effects.isAsync { + // Reject callback + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: skeleton.moduleName + ) ) - ) + // Resolve callback (typed per return type) + if function.returnType == .void { + closureSignatures.insert( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + } else { + closureSignatures.insert( + ClosureSignature( + parameters: [function.returnType], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + } + } + for type in file.types { + for method in type.methods where method.effects.isAsync { + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + if method.returnType == .void { + closureSignatures.insert( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + } else { + closureSignatures.insert( + ClosureSignature( + parameters: [method.returnType], + returnType: .void, + moduleName: skeleton.moduleName + ) + ) + } + } + } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 833355d3d..de0290f2d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -291,19 +291,26 @@ public struct ImportTS { func liftAsyncReturnValue(originalReturnType: BridgeType) { // For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32` // and returns void. The JS side calls the resolve/reject closures when the Promise settles. + // The resolve closure is typed to match the return type, so the ABI conversion is handled + // by the existing closure codegen infrastructure — no manual JSValue-to-type switch needed. abiReturnType = nil // Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise let innerBody = body body = CodeFragmentPrinter() + let rejectFactory = "makeRejectClosure: { JSTypedClosure<(JSValue) -> Void>($0) }" if originalReturnType == .void { + let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }" body.write( - "_ = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in" + "try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" ) } else { + let resolveSwiftType = originalReturnType.closureSwiftType + let resolveFactory = + "makeResolveClosure: { JSTypedClosure<(\(resolveSwiftType)) -> Void>($0) }" body.write( - "let resolved = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in" + "let resolved = try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" ) } body.indent { @@ -312,30 +319,7 @@ public struct ImportTS { body.write("}") if originalReturnType != .void { - let liftExpr: String - switch originalReturnType { - case .double: - liftExpr = "Double(resolved.number!)" - case .float: - liftExpr = "Float(resolved.number!)" - case .integer: - liftExpr = "Int(resolved.number!)" - case .string: - liftExpr = "resolved.string!" - case .bool: - liftExpr = "resolved.boolean!" - case .jsObject(let name): - if let name { - liftExpr = "\(name)(unsafelyWrapping: resolved.object!)" - } else { - liftExpr = "resolved.object!" - } - case .jsValue: - liftExpr = "resolved" - default: - liftExpr = "resolved.object!" - } - body.write("return \(liftExpr)") + body.write("return resolved") } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 4d81f8a46..ea2a20d08 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -641,22 +641,65 @@ public struct BridgeJSLink { walker.walk(unified) var closureSignatures = walker.visitor.signatures - // Inject (JSValue) -> Void closure signature when async imports exist + // Inject closure signatures for async import resolve/reject callbacks if let imported = unified.imported { - let hasAsyncImport = imported.children.contains { file in - file.functions.contains(where: { $0.effects.isAsync }) - || file.types.contains(where: { type in - type.methods.contains(where: { $0.effects.isAsync }) - }) - } - if hasAsyncImport { - closureSignatures.insert( - ClosureSignature( - parameters: [.jsValue], - returnType: .void, - moduleName: moduleName + for file in imported.children { + for function in file.functions where function.effects.isAsync { + // Reject callback + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: moduleName + ) ) - ) + // Resolve callback (typed per return type) + if function.returnType == .void { + closureSignatures.insert( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: moduleName + ) + ) + } else { + closureSignatures.insert( + ClosureSignature( + parameters: [function.returnType], + returnType: .void, + moduleName: moduleName + ) + ) + } + } + for type in file.types { + for method in type.methods where method.effects.isAsync { + closureSignatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: moduleName + ) + ) + if method.returnType == .void { + closureSignatures.insert( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: moduleName + ) + ) + } else { + closureSignatures.insert( + ClosureSignature( + parameters: [method.returnType], + returnType: .void, + moduleName: moduleName + ) + ) + } + } + } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift index 5985587f7..883f565e6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -59,6 +59,372 @@ public func _invoke_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: U #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule8JSObjectC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule8JSObjectC_y") +fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule8JSObjectC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSObject) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule8JSObjectC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSObject) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSObject) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule8JSObjectC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule8JSObjectC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule8JSObjectC_y") +public func _invoke_swift_closure_TestModule_10TestModule8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSObject) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSObject.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSS_y") +fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuleSS_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSS_y") +fileprivate func make_swift_closure_TestModule_10TestModuleSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuleSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuleSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuleSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (String) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_TestModule_10TestModuleSS_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuleSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSS_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuleSS_y") +public func _invoke_swift_closure_TestModule_10TestModuleSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSb_y") +fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuleSb_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSb_y") +fileprivate func make_swift_closure_TestModule_10TestModuleSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuleSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuleSb_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuleSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (Bool) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuleSb_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Bool) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuleSb_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSb_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuleSb_y") +public func _invoke_swift_closure_TestModule_10TestModuleSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Bool.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSd_y") +fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModuleSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSd_y") +fileprivate func make_swift_closure_TestModule_10TestModuleSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuleSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuleSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuleSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuleSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuleSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSd_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuleSd_y") +public func _invoke_swift_closure_TestModule_10TestModuleSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSi_y") +fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuleSi_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSi_y") +fileprivate func make_swift_closure_TestModule_10TestModuleSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuleSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSi_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuleSi_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuleSi_y { + static func bridgeJSLift(_ callbackId: Int32) -> (Int) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuleSi_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Int) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Int) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuleSi_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSi_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuleSi_y") +public func _invoke_swift_closure_TestModule_10TestModuleSi_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Int) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Int.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuley_y") +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuley_y(_ callback: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuley_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuley_y") +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuley_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuley_y { + static func bridgeJSLift(_ callbackId: Int32) -> () -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuley_y(callbackValue) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == () -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping () -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuley_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuley_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuley_y") +public func _invoke_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<() -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "TestModule", name: "bjs_asyncReturnVoid") fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void @@ -72,8 +438,10 @@ fileprivate func bjs_asyncReturnVoid_extern(_ resolveRef: Int32, _ rejectRef: In } func _$asyncReturnVoid() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_asyncReturnVoid(resolveRef, rejectRef) } @@ -92,13 +460,15 @@ fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: } func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(Int) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripInt(resolveRef, rejectRef, vValue) } - return Int(resolved.number!) + return resolved } #if arch(wasm32) @@ -114,14 +484,16 @@ fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRe } func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(String) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) } } - return resolved.string! + return resolved } #if arch(wasm32) @@ -137,13 +509,15 @@ fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: } func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(Bool) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripBool(resolveRef, rejectRef, vValue) } - return resolved.boolean! + return resolved } #if arch(wasm32) @@ -159,13 +533,15 @@ fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRe } func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripDouble(resolveRef, rejectRef, vValue) } - return Double(resolved.number!) + return resolved } #if arch(wasm32) @@ -181,11 +557,13 @@ fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ reject } func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(JSObject) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripJSObject(resolveRef, rejectRef, vValue) } - return resolved.object! + return resolved } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js index 0feffd08e..8eaf574a8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -329,6 +329,129 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule7JSValueV_y); } + bjs["invoke_js_callback_TestModule_10TestModule8JSObjectC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule8JSObjectC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule8JSObjectC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule8JSObjectC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule8JSObjectC_y); + } + bjs["invoke_js_callback_TestModule_10TestModuleSS_y"] = function(callbackId, param0Bytes, param0Count) { + try { + const callback = swift.memory.getObject(callbackId); + const string = decodeString(param0Bytes, param0Count); + callback(string); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuleSS_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuleSS_y = function(param0) { + const param0Bytes = textEncoder.encode(param0); + const param0Id = swift.memory.retain(param0Bytes); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSS_y(boxPtr, param0Id, param0Bytes.length); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSS_y); + } + bjs["invoke_js_callback_TestModule_10TestModuleSb_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0 !== 0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuleSb_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuleSb_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModuleSb_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSb_y); + } + bjs["invoke_js_callback_TestModule_10TestModuleSd_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuleSd_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuleSd_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModuleSd_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSd_y); + } + bjs["invoke_js_callback_TestModule_10TestModuleSi_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuleSi_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuleSi_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModuleSi_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSi_y); + } + bjs["invoke_js_callback_TestModule_10TestModuley_y"] = function(callbackId) { + try { + const callback = swift.memory.getObject(callbackId); + callback(); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuley_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuley_y = function() { + instance.exports.invoke_swift_closure_TestModule_10TestModuley_y(boxPtr); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuley_y); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(resolveRef, rejectRef) { const resolve = swift.memory.getObject(resolveRef); diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 5a5b2643c..e2a09b866 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -2087,35 +2087,51 @@ extension _BridgedAsOptional { // MARK: Async Promise Awaiting -/// Awaits a JavaScript Promise using `JSTypedClosure<(JSValue) -> Void>` callbacks. +/// Protocol for type-erasing `JSTypedClosure` in `_bjs_awaitPromise`. /// -/// The `makeClosure` factory is dependency-injected because this library cannot +/// The library cannot name concrete `JSTypedClosure<(Int) -> Void>` etc. because +/// those require per-module generated convenience inits. This protocol provides +/// access to the underlying JS object ref and cleanup. +@_spi(BridgeJS) public protocol _BridgeJSReleasableClosure { + var jsObject: JSObject { get } + func release() +} + +extension JSTypedClosure: _BridgeJSReleasableClosure {} + +/// Awaits a JavaScript Promise using typed resolve/reject `JSTypedClosure` callbacks. +/// +/// The closure factories are dependency-injected because this library cannot /// reference per-module generated `make_swift_closure_*` externs. The generated -/// code passes `{ JSTypedClosure($0) }` which uses the per-module convenience init. +/// code passes `{ JSTypedClosure<(T) -> Void>($0) }` which uses the per-module +/// convenience init. /// /// - Parameters: -/// - makeClosure: A factory that wraps a `(JSValue) -> Void` Swift closure -/// into a `JSTypedClosure`, creating the corresponding JS function. +/// - makeResolveClosure: A factory that wraps a `(T) -> Void` Swift closure +/// into a typed `JSTypedClosure`, creating the corresponding JS function. +/// - makeRejectClosure: A factory that wraps a `(JSValue) -> Void` Swift closure +/// into a `JSTypedClosure`, for the rejection path. /// - body: A closure that receives the resolve and reject JS object refs /// (as `Int32`) and should pass them to the appropriate JS extern function. -/// - Returns: The resolved `JSValue` from the Promise. +/// - Returns: The resolved value of type `T` from the Promise. /// - Throws: `JSException` if the Promise rejects. -@_spi(BridgeJS) public func _bjs_awaitPromise( - makeClosure: (@escaping (JSValue) -> Void) -> JSTypedClosure<(JSValue) -> Void>, +@_spi(BridgeJS) public func _bjs_awaitPromise( + makeResolveClosure: (@escaping (T) -> Void) -> R, + makeRejectClosure: (@escaping (JSValue) -> Void) -> E, _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void -) async throws(JSException) -> JSValue { - // Wrapper to send JSValue through the continuation. +) async throws(JSException) -> T { + // Wrapper to send the result through the continuation. // Safe in WebAssembly's single-threaded environment. struct Wrapper: @unchecked Sendable { - let result: Result + let result: Result } - var resolveClosure: JSTypedClosure<(JSValue) -> Void>? - var rejectClosure: JSTypedClosure<(JSValue) -> Void>? + var resolveClosure: R? + var rejectClosure: E? let wrapper: Wrapper = await withUnsafeContinuation { continuation in - resolveClosure = makeClosure { value in + resolveClosure = makeResolveClosure { value in continuation.resume(returning: Wrapper(result: .success(value))) } - rejectClosure = makeClosure { value in + rejectClosure = makeRejectClosure { value in continuation.resume(returning: Wrapper(result: .failure(JSException(value)))) } body( @@ -2127,3 +2143,36 @@ extension _BridgedAsOptional { rejectClosure?.release() return try wrapper.result.get() } + +/// Void-returning overload of `_bjs_awaitPromise`. +/// +/// Needed because `(Void) -> Void` is not the same as `() -> Void` in Swift (SE-0110), +/// so the generic overload cannot handle void returns. +@_spi(BridgeJS) public func _bjs_awaitPromise( + makeResolveClosure: (@escaping () -> Void) -> R, + makeRejectClosure: (@escaping (JSValue) -> Void) -> E, + _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void +) async throws(JSException) { + struct Wrapper: @unchecked Sendable { + let error: JSException? + } + var resolveClosure: R? + var rejectClosure: E? + let wrapper: Wrapper = await withUnsafeContinuation { continuation in + resolveClosure = makeResolveClosure { + continuation.resume(returning: Wrapper(error: nil)) + } + rejectClosure = makeRejectClosure { value in + continuation.resume(returning: Wrapper(error: JSException(value))) + } + body( + Int32(bitPattern: resolveClosure!.jsObject.id), + Int32(bitPattern: rejectClosure!.jsObject.id) + ) + } + resolveClosure?.release() + rejectClosure?.release() + if let error = wrapper.error { + throw error + } +} diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 26615d8a0..0f1f63c65 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -70,6 +70,67 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests10H #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTests11WeatherDataC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (WeatherData) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (WeatherData) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (WeatherData) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(WeatherData) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(WeatherData.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests13DataProcessorP_13DataProcessorP") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests13DataProcessorP_13DataProcessorP_extern(_ callback: Int32, _ param0: Int32) -> Int32 @@ -836,6 +897,129 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_ #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (String) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (Bool) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Bool) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Bool.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd_extern(_ callback: Int32, _ param0: Float64) -> Float64 @@ -899,6 +1083,67 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_ #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS_extern(_ callback: Int32, _ param0: Int32, _ param1Bytes: Int32, _ param1Length: Int32, _ param2: Float64) -> Int32 @@ -11199,8 +11444,10 @@ fileprivate func bjs_runAsyncWorks_extern(_ resolveRef: Int32, _ rejectRef: Int3 } func _$runAsyncWorks() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_runAsyncWorks(resolveRef, rejectRef) } @@ -11219,8 +11466,10 @@ fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ resolveRef: Int32, _ rejectRe } func _$jsAsyncRoundTripVoid() async throws(JSException) -> Void { - _ = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_jsAsyncRoundTripVoid(resolveRef, rejectRef) } @@ -11239,13 +11488,15 @@ fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ reject } func _$jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_jsAsyncRoundTripNumber(resolveRef, rejectRef, vValue) } - return Double(resolved.number!) + return resolved } #if arch(wasm32) @@ -11261,13 +11512,15 @@ fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRe } func _$jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(Bool) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_jsAsyncRoundTripBool(resolveRef, rejectRef, vValue) } - return resolved.boolean! + return resolved } #if arch(wasm32) @@ -11283,14 +11536,16 @@ fileprivate func bjs_jsAsyncRoundTripString_extern(_ resolveRef: Int32, _ reject } func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(String) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in bjs_jsAsyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) } } - return resolved.string! + return resolved } #if arch(wasm32) @@ -11306,14 +11561,16 @@ fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: I } func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { - let resolved = try await _bjs_awaitPromise(makeClosure: { - JSTypedClosure($0) + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(WeatherData) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(JSValue) -> Void>($0) }) { resolveRef, rejectRef in city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) } } - return WeatherData(unsafelyWrapping: resolved.object!) + return resolved } #if arch(wasm32) From 61fe95ed28e392feabd7d68de7b1b439ef947575 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 16:25:53 +0000 Subject: [PATCH 09/10] Make closure parameters as `sending` --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 21 +- .../Sources/BridgeJSCore/ImportTS.swift | 4 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 39 +- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 11 +- .../BridgeJSCodegenTests/AsyncImport.swift | 262 ++++---- .../ProtocolInClosure.json | 15 +- .../BridgeJSCodegenTests/SwiftClosure.json | 129 ++-- .../SwiftClosureImports.json | 6 +- .../BridgeJSLinkTests/AsyncImport.js | 104 +-- .../JavaScriptKit/BridgeJSIntrinsics.swift | 52 +- .../Generated/BridgeJS.swift | 632 +++++++++--------- .../Generated/JavaScript/BridgeJS.json | 162 +++-- 12 files changed, 759 insertions(+), 678 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index ecd354b7f..fcf71611b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -12,7 +12,10 @@ public struct ClosureCodegen { public init() {} private func swiftClosureType(for signature: ClosureSignature) -> String { - let closureParams = signature.parameters.map { "\($0.closureSwiftType)" }.joined(separator: ", ") + let sendingPrefix = signature.sendingParameters ? "sending " : "" + let closureParams = signature.parameters.map { "\(sendingPrefix)\($0.closureSwiftType)" }.joined( + separator: ", " + ) let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "") let swiftReturnType = signature.returnType.closureSwiftType return "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)" @@ -192,8 +195,10 @@ public struct ClosureCodegen { // When async imports exist, inject closure signatures for the typed resolve // and reject callbacks used by _bjs_awaitPromise. - // - Reject always uses (JSValue) -> Void + // - Reject always uses (sending JSValue) -> Void // - Resolve uses a typed closure matching the return type (or () -> Void for void) + // All async callback closures use `sending` parameters so values can be + // transferred through the checked continuation without Sendable constraints. if let imported = skeleton.imported { for file in imported.children { for function in file.functions where function.effects.isAsync { @@ -202,7 +207,8 @@ public struct ClosureCodegen { ClosureSignature( parameters: [.jsValue], returnType: .void, - moduleName: skeleton.moduleName + moduleName: skeleton.moduleName, + sendingParameters: true ) ) // Resolve callback (typed per return type) @@ -219,7 +225,8 @@ public struct ClosureCodegen { ClosureSignature( parameters: [function.returnType], returnType: .void, - moduleName: skeleton.moduleName + moduleName: skeleton.moduleName, + sendingParameters: true ) ) } @@ -230,7 +237,8 @@ public struct ClosureCodegen { ClosureSignature( parameters: [.jsValue], returnType: .void, - moduleName: skeleton.moduleName + moduleName: skeleton.moduleName, + sendingParameters: true ) ) if method.returnType == .void { @@ -246,7 +254,8 @@ public struct ClosureCodegen { ClosureSignature( parameters: [method.returnType], returnType: .void, - moduleName: skeleton.moduleName + moduleName: skeleton.moduleName, + sendingParameters: true ) ) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index de0290f2d..d709dd190 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -299,7 +299,7 @@ public struct ImportTS { let innerBody = body body = CodeFragmentPrinter() - let rejectFactory = "makeRejectClosure: { JSTypedClosure<(JSValue) -> Void>($0) }" + let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }" if originalReturnType == .void { let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }" body.write( @@ -308,7 +308,7 @@ public struct ImportTS { } else { let resolveSwiftType = originalReturnType.closureSwiftType let resolveFactory = - "makeResolveClosure: { JSTypedClosure<(\(resolveSwiftType)) -> Void>($0) }" + "makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }" body.write( "let resolved = try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index ea2a20d08..573693e4f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -641,7 +641,9 @@ public struct BridgeJSLink { walker.walk(unified) var closureSignatures = walker.visitor.signatures - // Inject closure signatures for async import resolve/reject callbacks + // Inject closure signatures for async import resolve/reject callbacks. + // All use sendingParameters: true so values can be transferred + // through checked continuations without Sendable constraints. if let imported = unified.imported { for file in imported.children { for function in file.functions where function.effects.isAsync { @@ -650,7 +652,8 @@ public struct BridgeJSLink { ClosureSignature( parameters: [.jsValue], returnType: .void, - moduleName: moduleName + moduleName: moduleName, + sendingParameters: true ) ) // Resolve callback (typed per return type) @@ -667,7 +670,8 @@ public struct BridgeJSLink { ClosureSignature( parameters: [function.returnType], returnType: .void, - moduleName: moduleName + moduleName: moduleName, + sendingParameters: true ) ) } @@ -678,7 +682,8 @@ public struct BridgeJSLink { ClosureSignature( parameters: [.jsValue], returnType: .void, - moduleName: moduleName + moduleName: moduleName, + sendingParameters: true ) ) if method.returnType == .void { @@ -694,7 +699,8 @@ public struct BridgeJSLink { ClosureSignature( parameters: [method.returnType], returnType: .void, - moduleName: moduleName + moduleName: moduleName, + sendingParameters: true ) ) } @@ -2296,20 +2302,18 @@ extension BridgeJSLink { /// Generates the call expression for an async import. /// - /// Instead of lowering the return value, this assigns the result to `promise` - /// and passes the resolve/reject closure refs to `.then`. + /// Chains `.then(resolve, reject)` directly on the returned Promise. func callAsync(name: String, fromObjectExpr: String) { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - body.write("const promise = \(callExpr);") - body.write("promise.then(resolve, reject);") + body.write("\(callExpr).then(resolve, reject);") } /// Renders an async import function with resolve/reject closure refs. /// /// The generated function takes `resolveRef` and `rejectRef` as the first parameters, - /// wraps the import call in try/catch, attaches Promise handlers, and - /// calls reject on synchronous errors. + /// looks up the resolve/reject closures from memory, and executes the body which + /// chains `.then(resolve, reject)` on the import's returned Promise. func renderAsyncFunction(name: String?) -> [String] { let printer = CodeFragmentPrinter() let allParams = ["resolveRef", "rejectRef"] + parameterNames @@ -2318,15 +2322,7 @@ extension BridgeJSLink { let s = JSGlueVariableScope.reservedSwift printer.write("const resolve = \(s).memory.getObject(resolveRef);") printer.write("const reject = \(s).memory.getObject(rejectRef);") - printer.write("try {") - printer.indent { - printer.write(contentsOf: body) - } - printer.write("} catch (error) {") - printer.indent { - printer.write("reject(error);") - } - printer.write("}") + printer.write(contentsOf: body) } printer.write("}") return printer.lines @@ -2390,8 +2386,7 @@ extension BridgeJSLink { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - body.write("const promise = \(callExpr);") - body.write("promise.then(resolve, reject);") + body.write("\(callExpr).then(resolve, reject);") } func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws -> String? { diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 9eea98edb..f61f7e2cf 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -101,24 +101,31 @@ public struct ClosureSignature: Codable, Equatable, Hashable, Sendable { public let isAsync: Bool public let isThrows: Bool public let moduleName: String + /// When true, closure parameters are annotated with `sending` in Swift. + /// Used for async Promise resolve/reject callbacks where values are + /// transferred through a continuation. + public let sendingParameters: Bool public init( parameters: [BridgeType], returnType: BridgeType, moduleName: String, isAsync: Bool = false, - isThrows: Bool = false + isThrows: Bool = false, + sendingParameters: Bool = false ) { self.parameters = parameters self.returnType = returnType self.moduleName = moduleName self.isAsync = isAsync self.isThrows = isThrows + self.sendingParameters = sendingParameters let paramPart = parameters.isEmpty ? "y" : parameters.map { $0.mangleTypeName }.joined() - let signaturePart = "\(paramPart)_\(returnType.mangleTypeName)" + let sendingPart = sendingParameters ? "s" : "" + let signaturePart = "\(sendingPart)\(paramPart)_\(returnType.mangleTypeName)" self.mangleName = "\(moduleName.count)\(moduleName)\(signaturePart)" } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift index 883f565e6..7a60bc6b7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.swift @@ -1,35 +1,35 @@ #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule7JSValueV_y") -fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules7JSValueV_y") +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { - return invoke_js_callback_TestModule_10TestModule7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule7JSValueV_y") -fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules7JSValueV_y") +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModule7JSValueV_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModule7JSValueV_y { - static func bridgeJSLift(_ callbackId: Int32) -> (JSValue) -> Void { +private enum _BJS_Closure_10TestModules7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() - invoke_js_callback_TestModule_10TestModule7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + invoke_js_callback_TestModule_10TestModules7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) #else fatalError("Only available on WebAssembly") #endif @@ -37,10 +37,10 @@ private enum _BJS_Closure_10TestModule7JSValueV_y { } } -extension JSTypedClosure where Signature == (JSValue) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSValue) -> Void) { +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModule7JSValueV_y, + makeClosure: make_swift_closure_TestModule_10TestModules7JSValueV_y, body: body, fileID: fileID, line: line @@ -48,11 +48,11 @@ extension JSTypedClosure where Signature == (JSValue) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule7JSValueV_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModule7JSValueV_y") -public func _invoke_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +public func _invoke_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) #else fatalError("Only available on WebAssembly") @@ -60,37 +60,37 @@ public func _invoke_swift_closure_TestModule_10TestModule7JSValueV_y(_ boxPtr: U } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule8JSObjectC_y") -fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules8JSObjectC_y") +fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule8JSObjectC_y(_ callback: Int32, _ param0: Int32) -> Void { - return invoke_js_callback_TestModule_10TestModule8JSObjectC_y_extern(callback, param0) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules8JSObjectC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModules8JSObjectC_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule8JSObjectC_y") -fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules8JSObjectC_y") +fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModule8JSObjectC_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules8JSObjectC_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModule8JSObjectC_y { - static func bridgeJSLift(_ callbackId: Int32) -> (JSObject) -> Void { +private enum _BJS_Closure_10TestModules8JSObjectC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSObject) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_TestModule_10TestModule8JSObjectC_y(callbackValue, param0Value) + invoke_js_callback_TestModule_10TestModules8JSObjectC_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -98,10 +98,10 @@ private enum _BJS_Closure_10TestModule8JSObjectC_y { } } -extension JSTypedClosure where Signature == (JSObject) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSObject) -> Void) { +extension JSTypedClosure where Signature == (sending JSObject) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSObject) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModule8JSObjectC_y, + makeClosure: make_swift_closure_TestModule_10TestModules8JSObjectC_y, body: body, fileID: fileID, line: line @@ -109,11 +109,11 @@ extension JSTypedClosure where Signature == (JSObject) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule8JSObjectC_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModule8JSObjectC_y") -public func _invoke_swift_closure_TestModule_10TestModule8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules8JSObjectC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules8JSObjectC_y") +public func _invoke_swift_closure_TestModule_10TestModules8JSObjectC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSObject) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSObject) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(JSObject.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") @@ -121,37 +121,37 @@ public func _invoke_swift_closure_TestModule_10TestModule8JSObjectC_y(_ boxPtr: } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSS_y") -fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSS_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { - return invoke_js_callback_TestModule_10TestModuleSS_y_extern(callback, param0Bytes, param0Length) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSS_y_extern(callback, param0Bytes, param0Length) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSS_y") -fileprivate func make_swift_closure_TestModule_10TestModuleSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSS_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModuleSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModulesSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModuleSS_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSS_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModuleSS_y { - static func bridgeJSLift(_ callbackId: Int32) -> (String) -> Void { +private enum _BJS_Closure_10TestModulesSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending String) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in - invoke_js_callback_TestModule_10TestModuleSS_y(callbackValue, param0Bytes, param0Length) + invoke_js_callback_TestModule_10TestModulesSS_y(callbackValue, param0Bytes, param0Length) } #else fatalError("Only available on WebAssembly") @@ -160,10 +160,10 @@ private enum _BJS_Closure_10TestModuleSS_y { } } -extension JSTypedClosure where Signature == (String) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Void) { +extension JSTypedClosure where Signature == (sending String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending String) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModuleSS_y, + makeClosure: make_swift_closure_TestModule_10TestModulesSS_y, body: body, fileID: fileID, line: line @@ -171,11 +171,11 @@ extension JSTypedClosure where Signature == (String) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSS_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModuleSS_y") -public func _invoke_swift_closure_TestModule_10TestModuleSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSS_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSS_y") +public func _invoke_swift_closure_TestModule_10TestModulesSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) #else fatalError("Only available on WebAssembly") @@ -183,37 +183,37 @@ public func _invoke_swift_closure_TestModule_10TestModuleSS_y(_ boxPtr: UnsafeMu } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSb_y") -fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSb_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSb_y(_ callback: Int32, _ param0: Int32) -> Void { - return invoke_js_callback_TestModule_10TestModuleSb_y_extern(callback, param0) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSb_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSb_y") -fileprivate func make_swift_closure_TestModule_10TestModuleSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSb_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModuleSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModulesSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModuleSb_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSb_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModuleSb_y { - static func bridgeJSLift(_ callbackId: Int32) -> (Bool) -> Void { +private enum _BJS_Closure_10TestModulesSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Bool) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_TestModule_10TestModuleSb_y(callbackValue, param0Value) + invoke_js_callback_TestModule_10TestModulesSb_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -221,10 +221,10 @@ private enum _BJS_Closure_10TestModuleSb_y { } } -extension JSTypedClosure where Signature == (Bool) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Bool) -> Void) { +extension JSTypedClosure where Signature == (sending Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Bool) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModuleSb_y, + makeClosure: make_swift_closure_TestModule_10TestModulesSb_y, body: body, fileID: fileID, line: line @@ -232,11 +232,11 @@ extension JSTypedClosure where Signature == (Bool) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSb_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModuleSb_y") -public func _invoke_swift_closure_TestModule_10TestModuleSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSb_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSb_y") +public func _invoke_swift_closure_TestModule_10TestModulesSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(Bool.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") @@ -244,37 +244,37 @@ public func _invoke_swift_closure_TestModule_10TestModuleSb_y(_ boxPtr: UnsafeMu } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSd_y") -fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSd_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSd_y(_ callback: Int32, _ param0: Float64) -> Void { - return invoke_js_callback_TestModule_10TestModuleSd_y_extern(callback, param0) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModulesSd_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSd_y") -fileprivate func make_swift_closure_TestModule_10TestModuleSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSd_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModuleSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModuleSd_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSd_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModuleSd_y { - static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Void { +private enum _BJS_Closure_10TestModulesSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_TestModule_10TestModuleSd_y(callbackValue, param0Value) + invoke_js_callback_TestModule_10TestModulesSd_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -282,10 +282,10 @@ private enum _BJS_Closure_10TestModuleSd_y { } } -extension JSTypedClosure where Signature == (Double) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Void) { +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModuleSd_y, + makeClosure: make_swift_closure_TestModule_10TestModulesSd_y, body: body, fileID: fileID, line: line @@ -293,11 +293,11 @@ extension JSTypedClosure where Signature == (Double) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSd_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModuleSd_y") -public func _invoke_swift_closure_TestModule_10TestModuleSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSd_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSd_y") +public func _invoke_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(Double.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") @@ -305,37 +305,37 @@ public func _invoke_swift_closure_TestModule_10TestModuleSd_y(_ boxPtr: UnsafeMu } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSi_y") -fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSi_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void #else -fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void { +fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y_extern(_ callback: Int32, _ param0: Int32) -> Void { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSi_y(_ callback: Int32, _ param0: Int32) -> Void { - return invoke_js_callback_TestModule_10TestModuleSi_y_extern(callback, param0) +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSi_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModulesSi_y_extern(callback, param0) } #if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuleSi_y") -fileprivate func make_swift_closure_TestModule_10TestModuleSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSi_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 #else -fileprivate func make_swift_closure_TestModule_10TestModuleSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { +fileprivate func make_swift_closure_TestModule_10TestModulesSi_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { fatalError("Only available on WebAssembly") } #endif -@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuleSi_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_TestModule_10TestModuleSi_y_extern(boxPtr, file, line) +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSi_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSi_y_extern(boxPtr, file, line) } -private enum _BJS_Closure_10TestModuleSi_y { - static func bridgeJSLift(_ callbackId: Int32) -> (Int) -> Void { +private enum _BJS_Closure_10TestModulesSi_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Int) -> Void { let callback = JSObject.bridgeJSLiftParameter(callbackId) return { [callback] param0 in #if arch(wasm32) let callbackValue = callback.bridgeJSLowerParameter() let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_TestModule_10TestModuleSi_y(callbackValue, param0Value) + invoke_js_callback_TestModule_10TestModulesSi_y(callbackValue, param0Value) #else fatalError("Only available on WebAssembly") #endif @@ -343,10 +343,10 @@ private enum _BJS_Closure_10TestModuleSi_y { } } -extension JSTypedClosure where Signature == (Int) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Int) -> Void) { +extension JSTypedClosure where Signature == (sending Int) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Int) -> Void) { self.init( - makeClosure: make_swift_closure_TestModule_10TestModuleSi_y, + makeClosure: make_swift_closure_TestModule_10TestModulesSi_y, body: body, fileID: fileID, line: line @@ -354,11 +354,11 @@ extension JSTypedClosure where Signature == (Int) -> Void { } } -@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuleSi_y") -@_cdecl("invoke_swift_closure_TestModule_10TestModuleSi_y") -public func _invoke_swift_closure_TestModule_10TestModuleSi_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSi_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSi_y") +public func _invoke_swift_closure_TestModule_10TestModulesSi_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Int) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Int) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure closure(Int.bridgeJSLiftParameter(param0)) #else fatalError("Only available on WebAssembly") @@ -441,7 +441,7 @@ func _$asyncReturnVoid() async throws(JSException) -> Void { try await _bjs_awaitPromise(makeResolveClosure: { JSTypedClosure<() -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_asyncReturnVoid(resolveRef, rejectRef) } @@ -461,9 +461,9 @@ fileprivate func bjs_asyncRoundTripInt_extern(_ resolveRef: Int32, _ rejectRef: func _$asyncRoundTripInt(_ v: Int) async throws(JSException) -> Int { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(Int) -> Void>($0) + JSTypedClosure<(sending Int) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripInt(resolveRef, rejectRef, vValue) @@ -485,9 +485,9 @@ fileprivate func bjs_asyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRe func _$asyncRoundTripString(_ v: String) async throws(JSException) -> String { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(String) -> Void>($0) + JSTypedClosure<(sending String) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) @@ -510,9 +510,9 @@ fileprivate func bjs_asyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: func _$asyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(Bool) -> Void>($0) + JSTypedClosure<(sending Bool) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripBool(resolveRef, rejectRef, vValue) @@ -534,9 +534,9 @@ fileprivate func bjs_asyncRoundTripDouble_extern(_ resolveRef: Int32, _ rejectRe func _$asyncRoundTripDouble(_ v: Double) async throws(JSException) -> Double { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(Double) -> Void>($0) + JSTypedClosure<(sending Double) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripDouble(resolveRef, rejectRef, vValue) @@ -558,9 +558,9 @@ fileprivate func bjs_asyncRoundTripJSObject_extern(_ resolveRef: Int32, _ reject func _$asyncRoundTripJSObject(_ v: JSObject) async throws(JSException) -> JSObject { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(JSObject) -> Void>($0) + JSTypedClosure<(sending JSObject) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_asyncRoundTripJSObject(resolveRef, rejectRef, vValue) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json index 4ba7ba9a5..36d6941d3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ProtocolInClosure.json @@ -84,7 +84,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -130,7 +131,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -166,7 +168,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -191,7 +194,8 @@ "swiftProtocol" : { "_0" : "Renderable" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -232,7 +236,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json index f610d4bde..41662e48b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json @@ -61,7 +61,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -313,7 +314,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -338,7 +340,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -380,7 +383,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -411,7 +415,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -447,7 +452,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -472,7 +478,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -508,7 +515,8 @@ "float" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -533,7 +541,8 @@ "float" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -569,7 +578,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -594,7 +604,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -640,7 +651,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -675,7 +687,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -727,7 +740,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -768,7 +782,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -814,7 +829,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -849,7 +865,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -895,7 +912,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -930,7 +948,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -976,7 +995,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1011,7 +1031,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1047,7 +1068,8 @@ "swiftHeapObject" : { "_0" : "Person" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1072,7 +1094,8 @@ "swiftHeapObject" : { "_0" : "Person" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1118,7 +1141,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1153,7 +1177,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1189,7 +1214,8 @@ "caseEnum" : { "_0" : "Direction" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1214,7 +1240,8 @@ "caseEnum" : { "_0" : "Direction" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1252,7 +1279,8 @@ "_0" : "Theme", "_1" : "String" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1279,7 +1307,8 @@ "_0" : "Theme", "_1" : "String" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1317,7 +1346,8 @@ "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1344,7 +1374,8 @@ "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1380,7 +1411,8 @@ "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1405,7 +1437,8 @@ "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1451,7 +1484,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1486,7 +1520,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1534,7 +1569,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1571,7 +1607,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1619,7 +1656,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1656,7 +1694,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1702,7 +1741,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1737,7 +1777,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1783,7 +1824,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -1818,7 +1860,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json index 0e348a44e..a78b1bf5d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json @@ -48,7 +48,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -108,7 +109,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js index 8eaf574a8..89ab29827 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -307,7 +307,7 @@ export async function createInstantiator(options, swift) { const func = swift.memory.getObject(funcRef); func.__unregister(); } - bjs["invoke_js_callback_TestModule_10TestModule7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { + bjs["invoke_js_callback_TestModule_10TestModules7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { try { const callback = swift.memory.getObject(callbackId); const jsValue = __bjs_jsValueLift(param0Kind, param0Payload1, param0Payload2); @@ -316,10 +316,10 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModule7JSValueV_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModule7JSValueV_y = function(param0) { + bjs["make_swift_closure_TestModule_10TestModules7JSValueV_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules7JSValueV_y = function(param0) { const [param0Kind, param0Payload1, param0Payload2] = __bjs_jsValueLower(param0); - instance.exports.invoke_swift_closure_TestModule_10TestModule7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); + instance.exports.invoke_swift_closure_TestModule_10TestModules7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -327,9 +327,9 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule7JSValueV_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules7JSValueV_y); } - bjs["invoke_js_callback_TestModule_10TestModule8JSObjectC_y"] = function(callbackId, param0) { + bjs["invoke_js_callback_TestModule_10TestModules8JSObjectC_y"] = function(callbackId, param0) { try { const callback = swift.memory.getObject(callbackId); callback(swift.memory.getObject(param0)); @@ -337,9 +337,9 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModule8JSObjectC_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModule8JSObjectC_y = function(param0) { - instance.exports.invoke_swift_closure_TestModule_10TestModule8JSObjectC_y(boxPtr, swift.memory.retain(param0)); + bjs["make_swift_closure_TestModule_10TestModules8JSObjectC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules8JSObjectC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModules8JSObjectC_y(boxPtr, swift.memory.retain(param0)); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -347,9 +347,9 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule8JSObjectC_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules8JSObjectC_y); } - bjs["invoke_js_callback_TestModule_10TestModuleSS_y"] = function(callbackId, param0Bytes, param0Count) { + bjs["invoke_js_callback_TestModule_10TestModulesSS_y"] = function(callbackId, param0Bytes, param0Count) { try { const callback = swift.memory.getObject(callbackId); const string = decodeString(param0Bytes, param0Count); @@ -358,11 +358,11 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModuleSS_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModuleSS_y = function(param0) { + bjs["make_swift_closure_TestModule_10TestModulesSS_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSS_y = function(param0) { const param0Bytes = textEncoder.encode(param0); const param0Id = swift.memory.retain(param0Bytes); - instance.exports.invoke_swift_closure_TestModule_10TestModuleSS_y(boxPtr, param0Id, param0Bytes.length); + instance.exports.invoke_swift_closure_TestModule_10TestModulesSS_y(boxPtr, param0Id, param0Bytes.length); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -370,9 +370,9 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSS_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSS_y); } - bjs["invoke_js_callback_TestModule_10TestModuleSb_y"] = function(callbackId, param0) { + bjs["invoke_js_callback_TestModule_10TestModulesSb_y"] = function(callbackId, param0) { try { const callback = swift.memory.getObject(callbackId); callback(param0 !== 0); @@ -380,9 +380,9 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModuleSb_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModuleSb_y = function(param0) { - instance.exports.invoke_swift_closure_TestModule_10TestModuleSb_y(boxPtr, param0); + bjs["make_swift_closure_TestModule_10TestModulesSb_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSb_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSb_y(boxPtr, param0); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -390,9 +390,9 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSb_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSb_y); } - bjs["invoke_js_callback_TestModule_10TestModuleSd_y"] = function(callbackId, param0) { + bjs["invoke_js_callback_TestModule_10TestModulesSd_y"] = function(callbackId, param0) { try { const callback = swift.memory.getObject(callbackId); callback(param0); @@ -400,9 +400,9 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModuleSd_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModuleSd_y = function(param0) { - instance.exports.invoke_swift_closure_TestModule_10TestModuleSd_y(boxPtr, param0); + bjs["make_swift_closure_TestModule_10TestModulesSd_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSd_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSd_y(boxPtr, param0); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -410,9 +410,9 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSd_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSd_y); } - bjs["invoke_js_callback_TestModule_10TestModuleSi_y"] = function(callbackId, param0) { + bjs["invoke_js_callback_TestModule_10TestModulesSi_y"] = function(callbackId, param0) { try { const callback = swift.memory.getObject(callbackId); callback(param0); @@ -420,9 +420,9 @@ export async function createInstantiator(options, swift) { setException(error); } } - bjs["make_swift_closure_TestModule_10TestModuleSi_y"] = function(boxPtr, file, line) { - const lower_closure_TestModule_10TestModuleSi_y = function(param0) { - instance.exports.invoke_swift_closure_TestModule_10TestModuleSi_y(boxPtr, param0); + bjs["make_swift_closure_TestModule_10TestModulesSi_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSi_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSi_y(boxPtr, param0); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -430,7 +430,7 @@ export async function createInstantiator(options, swift) { throw error; } }; - return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSi_y); + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSi_y); } bjs["invoke_js_callback_TestModule_10TestModuley_y"] = function(callbackId) { try { @@ -456,63 +456,33 @@ export async function createInstantiator(options, swift) { TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid(resolveRef, rejectRef) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const promise = imports.asyncReturnVoid(); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + imports.asyncReturnVoid().then(resolve, reject); } TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(resolveRef, rejectRef, v) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const promise = imports.asyncRoundTripInt(v); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + imports.asyncRoundTripInt(v).then(resolve, reject); } TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(resolveRef, rejectRef, vBytes, vCount) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const string = decodeString(vBytes, vCount); - const promise = imports.asyncRoundTripString(string); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + const string = decodeString(vBytes, vCount); + imports.asyncRoundTripString(string).then(resolve, reject); } TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(resolveRef, rejectRef, v) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const promise = imports.asyncRoundTripBool(v !== 0); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + imports.asyncRoundTripBool(v !== 0).then(resolve, reject); } TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(resolveRef, rejectRef, v) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const promise = imports.asyncRoundTripDouble(v); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + imports.asyncRoundTripDouble(v).then(resolve, reject); } TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(resolveRef, rejectRef, v) { const resolve = swift.memory.getObject(resolveRef); const reject = swift.memory.getObject(rejectRef); - try { - const promise = imports.asyncRoundTripJSObject(swift.memory.getObject(v)); - promise.then(resolve, reject); - } catch (error) { - reject(error); - } + imports.asyncRoundTripJSObject(swift.memory.getObject(v)).then(resolve, reject); } }, setInstance: (i) => { diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index e2a09b866..867d0e835 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -2097,7 +2097,7 @@ extension _BridgedAsOptional { func release() } -extension JSTypedClosure: _BridgeJSReleasableClosure {} +@_spi(BridgeJS) extension JSTypedClosure: _BridgeJSReleasableClosure {} /// Awaits a JavaScript Promise using typed resolve/reject `JSTypedClosure` callbacks. /// @@ -2116,32 +2116,29 @@ extension JSTypedClosure: _BridgeJSReleasableClosure {} /// - Returns: The resolved value of type `T` from the Promise. /// - Throws: `JSException` if the Promise rejects. @_spi(BridgeJS) public func _bjs_awaitPromise( - makeResolveClosure: (@escaping (T) -> Void) -> R, - makeRejectClosure: (@escaping (JSValue) -> Void) -> E, + makeResolveClosure: (@escaping (sending T) -> Void) -> R, + makeRejectClosure: (@escaping (sending JSValue) -> Void) -> E, _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void ) async throws(JSException) -> T { - // Wrapper to send the result through the continuation. - // Safe in WebAssembly's single-threaded environment. - struct Wrapper: @unchecked Sendable { - let result: Result - } var resolveClosure: R? var rejectClosure: E? - let wrapper: Wrapper = await withUnsafeContinuation { continuation in - resolveClosure = makeResolveClosure { value in - continuation.resume(returning: Wrapper(result: .success(value))) + let result: Result = await withCheckedContinuation { continuation in + let resolve = makeResolveClosure { value in + continuation.resume(returning: .success(value)) } - rejectClosure = makeRejectClosure { value in - continuation.resume(returning: Wrapper(result: .failure(JSException(value)))) + let reject = makeRejectClosure { value in + continuation.resume(returning: .failure(JSException(value))) } + resolveClosure = resolve + rejectClosure = reject body( - Int32(bitPattern: resolveClosure!.jsObject.id), - Int32(bitPattern: rejectClosure!.jsObject.id) + Int32(bitPattern: resolve.jsObject.id), + Int32(bitPattern: reject.jsObject.id) ) } resolveClosure?.release() rejectClosure?.release() - return try wrapper.result.get() + return try result.get() } /// Void-returning overload of `_bjs_awaitPromise`. @@ -2150,29 +2147,28 @@ extension JSTypedClosure: _BridgeJSReleasableClosure {} /// so the generic overload cannot handle void returns. @_spi(BridgeJS) public func _bjs_awaitPromise( makeResolveClosure: (@escaping () -> Void) -> R, - makeRejectClosure: (@escaping (JSValue) -> Void) -> E, + makeRejectClosure: (@escaping (sending JSValue) -> Void) -> E, _ body: (_ resolveRef: Int32, _ rejectRef: Int32) -> Void ) async throws(JSException) { - struct Wrapper: @unchecked Sendable { - let error: JSException? - } var resolveClosure: R? var rejectClosure: E? - let wrapper: Wrapper = await withUnsafeContinuation { continuation in - resolveClosure = makeResolveClosure { - continuation.resume(returning: Wrapper(error: nil)) + let error: JSException? = await withCheckedContinuation { continuation in + let resolve = makeResolveClosure { + continuation.resume(returning: nil) } - rejectClosure = makeRejectClosure { value in - continuation.resume(returning: Wrapper(error: JSException(value))) + let reject = makeRejectClosure { value in + continuation.resume(returning: JSException(value)) } + resolveClosure = resolve + rejectClosure = reject body( - Int32(bitPattern: resolveClosure!.jsObject.id), - Int32(bitPattern: rejectClosure!.jsObject.id) + Int32(bitPattern: resolve.jsObject.id), + Int32(bitPattern: reject.jsObject.id) ) } resolveClosure?.release() rejectClosure?.release() - if let error = wrapper.error { + if let error { throw error } } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 613225d6d..b7d6b55c3 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -70,67 +70,6 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests10H #endif } -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void -#else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ callback: Int32, _ param0: Int32) -> Void { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(callback, param0) -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 -#else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y_extern(boxPtr, file, line) -} - -private enum _BJS_Closure_20BridgeJSRuntimeTests11WeatherDataC_y { - static func bridgeJSLift(_ callbackId: Int32) -> (WeatherData) -> Void { - let callback = JSObject.bridgeJSLiftParameter(callbackId) - return { [callback] param0 in - #if arch(wasm32) - let callbackValue = callback.bridgeJSLowerParameter() - let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(callbackValue, param0Value) - #else - fatalError("Only available on WebAssembly") - #endif - } - } -} - -extension JSTypedClosure where Signature == (WeatherData) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (WeatherData) -> Void) { - self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y, - body: body, - fileID: fileID, - line: line - ) - } -} - -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { - #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(WeatherData) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure - closure(WeatherData.bridgeJSLiftParameter(param0)) - #else - fatalError("Only available on WebAssembly") - #endif -} - #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests13DataProcessorP_13DataProcessorP") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests13DataProcessorP_13DataProcessorP_extern(_ callback: Int32, _ param0: Int32) -> Int32 @@ -452,67 +391,6 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7Gr #endif } -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void -#else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 -#else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y_extern(boxPtr, file, line) -} - -private enum _BJS_Closure_20BridgeJSRuntimeTests7JSValueV_y { - static func bridgeJSLift(_ callbackId: Int32) -> (JSValue) -> Void { - let callback = JSObject.bridgeJSLiftParameter(callbackId) - return { [callback] param0 in - #if arch(wasm32) - let callbackValue = callback.bridgeJSLowerParameter() - let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() - invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) - #else - fatalError("Only available on WebAssembly") - #endif - } - } -} - -extension JSTypedClosure where Signature == (JSValue) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSValue) -> Void) { - self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y, - body: body, - fileID: fileID, - line: line - ) - } -} - -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { - #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure - closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) - #else - fatalError("Only available on WebAssembly") - #endif -} - #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests8JSObjectC_8JSObjectC") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTests8JSObjectC_8JSObjectC_extern(_ callback: Int32, _ param0: Int32) -> Int32 @@ -897,129 +775,6 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_ #endif } -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void -#else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(callback, param0Bytes, param0Length) -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 -#else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y_extern(boxPtr, file, line) -} - -private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_y { - static func bridgeJSLift(_ callbackId: Int32) -> (String) -> Void { - let callback = JSObject.bridgeJSLiftParameter(callbackId) - return { [callback] param0 in - #if arch(wasm32) - let callbackValue = callback.bridgeJSLowerParameter() - param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in - invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(callbackValue, param0Bytes, param0Length) - } - #else - fatalError("Only available on WebAssembly") - #endif - } - } -} - -extension JSTypedClosure where Signature == (String) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Void) { - self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y, - body: body, - fileID: fileID, - line: line - ) - } -} - -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { - #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure - closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) - #else - fatalError("Only available on WebAssembly") - #endif -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void -#else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ callback: Int32, _ param0: Int32) -> Void { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(callback, param0) -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 -#else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y_extern(boxPtr, file, line) -} - -private enum _BJS_Closure_20BridgeJSRuntimeTestsSb_y { - static func bridgeJSLift(_ callbackId: Int32) -> (Bool) -> Void { - let callback = JSObject.bridgeJSLiftParameter(callbackId) - return { [callback] param0 in - #if arch(wasm32) - let callbackValue = callback.bridgeJSLowerParameter() - let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(callbackValue, param0Value) - #else - fatalError("Only available on WebAssembly") - #endif - } - } -} - -extension JSTypedClosure where Signature == (Bool) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Bool) -> Void) { - self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y, - body: body, - fileID: fileID, - line: line - ) - } -} - -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { - #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure - closure(Bool.bridgeJSLiftParameter(param0)) - #else - fatalError("Only available on WebAssembly") - #endif -} - #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_Sd_extern(_ callback: Int32, _ param0: Float64) -> Float64 @@ -1083,67 +838,6 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_ #endif } -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void -#else -fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ callback: Int32, _ param0: Float64) -> Void { - return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(callback, param0) -} - -#if arch(wasm32) -@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 -#else -fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { - return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y_extern(boxPtr, file, line) -} - -private enum _BJS_Closure_20BridgeJSRuntimeTestsSd_y { - static func bridgeJSLift(_ callbackId: Int32) -> (Double) -> Void { - let callback = JSObject.bridgeJSLiftParameter(callbackId) - return { [callback] param0 in - #if arch(wasm32) - let callbackValue = callback.bridgeJSLowerParameter() - let param0Value = param0.bridgeJSLowerParameter() - invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(callbackValue, param0Value) - #else - fatalError("Only available on WebAssembly") - #endif - } - } -} - -extension JSTypedClosure where Signature == (Double) -> Void { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Double) -> Void) { - self.init( - makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y, - body: body, - fileID: fileID, - line: line - ) - } -} - -@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") -@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { - #if arch(wasm32) - let closure = Unmanaged<_BridgeJSTypedClosureBox<(Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure - closure(Double.bridgeJSLiftParameter(param0)) - #else - fatalError("Only available on WebAssembly") - #endif -} - #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSiSSSd_SS_extern(_ callback: Int32, _ param0: Int32, _ param1Bytes: Int32, _ param1Length: Int32, _ param2: Float64) -> Int32 @@ -1980,6 +1674,312 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqS #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss11WeatherDataC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending WeatherData) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending WeatherData) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending WeatherData) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending WeatherData) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(WeatherData.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending String) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending String) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending String) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending String) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(String.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Bool) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Bool) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Bool) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Bool) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Bool.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsy_13DataProcessorP") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsy_13DataProcessorP_extern(_ callback: Int32) -> Int32 @@ -11616,7 +11616,7 @@ func _$runAsyncWorks() async throws(JSException) -> Void { try await _bjs_awaitPromise(makeResolveClosure: { JSTypedClosure<() -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_runAsyncWorks(resolveRef, rejectRef) } @@ -11638,7 +11638,7 @@ func _$jsAsyncRoundTripVoid() async throws(JSException) -> Void { try await _bjs_awaitPromise(makeResolveClosure: { JSTypedClosure<() -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in bjs_jsAsyncRoundTripVoid(resolveRef, rejectRef) } @@ -11658,9 +11658,9 @@ fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ reject func _$jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(Double) -> Void>($0) + JSTypedClosure<(sending Double) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_jsAsyncRoundTripNumber(resolveRef, rejectRef, vValue) @@ -11682,9 +11682,9 @@ fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRe func _$jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(Bool) -> Void>($0) + JSTypedClosure<(sending Bool) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in let vValue = v.bridgeJSLowerParameter() bjs_jsAsyncRoundTripBool(resolveRef, rejectRef, vValue) @@ -11706,9 +11706,9 @@ fileprivate func bjs_jsAsyncRoundTripString_extern(_ resolveRef: Int32, _ reject func _$jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(String) -> Void>($0) + JSTypedClosure<(sending String) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in v.bridgeJSWithLoweredParameter { (vBytes, vLength) in bjs_jsAsyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) @@ -11731,9 +11731,9 @@ fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: I func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(WeatherData) -> Void>($0) + JSTypedClosure<(sending WeatherData) -> Void>($0) }, makeRejectClosure: { - JSTypedClosure<(JSValue) -> Void>($0) + JSTypedClosure<(sending JSValue) -> Void>($0) }) { resolveRef, rejectRef in city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 5a4f120f8..c8c25c492 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -49,7 +49,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -97,7 +98,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -145,7 +147,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -202,7 +205,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -250,7 +254,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -298,7 +303,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -591,7 +597,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -641,7 +648,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -684,7 +692,8 @@ "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -724,7 +733,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -2931,7 +2941,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3017,7 +3028,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3059,7 +3071,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3100,7 +3113,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3151,7 +3165,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3199,7 +3214,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3246,7 +3262,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3282,7 +3299,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3318,7 +3336,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3362,7 +3381,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3409,7 +3429,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3455,7 +3476,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3497,7 +3519,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3533,7 +3556,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3572,7 +3596,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3607,7 +3632,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3648,7 +3674,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3697,7 +3724,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3745,7 +3773,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3792,7 +3821,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3828,7 +3858,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3866,7 +3897,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3902,7 +3934,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3927,7 +3960,8 @@ "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -3968,7 +4002,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -10604,7 +10639,8 @@ }, "_1" : "null" } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13606,7 +13642,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13656,7 +13693,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -13708,7 +13746,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -16987,7 +17026,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17024,7 +17064,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17082,7 +17123,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17134,7 +17176,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17183,7 +17226,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17232,7 +17276,8 @@ "jsObject" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17289,7 +17334,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -17330,7 +17376,8 @@ "double" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -17371,7 +17418,8 @@ "string" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : false } @@ -17419,7 +17467,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17477,7 +17526,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17543,7 +17593,8 @@ "width" : "word" } } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17583,7 +17634,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17622,7 +17674,8 @@ "bool" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } @@ -17662,7 +17715,8 @@ "void" : { } - } + }, + "sendingParameters" : false }, "useJSTypedClosure" : true } From 02a3faefa0460f4aa3ff14ca384f85b1c7abf5bd Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Mar 2026 22:29:39 +0000 Subject: [PATCH 10/10] Fix issues discovered during BridgeJS plugin usage --- .../BridgeJSBuildPlugin.swift | 19 +++++++++++++++---- .../BridgeJSCommandPlugin.swift | 9 +++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift index 3cb6dc860..0236039d3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift @@ -20,9 +20,11 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { private func createGenerateCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command { let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "BridgeJS.swift") + let outputMacrosSwiftPath = context.pluginWorkDirectoryURL.appending(path: "BridgeJS.Macros.swift") let inputSwiftFiles = target.sourceFiles.filter { - !$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/") + $0.url.pathExtension == "swift" + && !$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/") } .map(\.url) @@ -42,6 +44,7 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { inputFiles.append(contentsOf: pluginGeneratedSwiftFiles) let inputTSFile = target.directoryURL.appending(path: "bridge-js.d.ts") + let inputGlobalTSFile = target.directoryURL.appending(path: "bridge-js.global.d.ts") let tsconfigPath = context.package.directoryURL.appending(path: "tsconfig.json") var arguments: [String] = [ @@ -55,8 +58,16 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { "--always-write", "true", ] - if FileManager.default.fileExists(atPath: inputTSFile.path) { - inputFiles.append(contentsOf: [inputTSFile, tsconfigPath]) + let hasDts = FileManager.default.fileExists(atPath: inputTSFile.path) + let hasGlobalDts = FileManager.default.fileExists(atPath: inputGlobalTSFile.path) + if hasDts || hasGlobalDts { + if hasDts { + inputFiles.append(inputTSFile) + } + if hasGlobalDts { + inputFiles.append(inputGlobalTSFile) + } + inputFiles.append(tsconfigPath) arguments.append(contentsOf: [ "--project", tsconfigPath.path, @@ -71,7 +82,7 @@ struct BridgeJSBuildPlugin: BuildToolPlugin { executable: try context.tool(named: "BridgeJSTool").url, arguments: arguments, inputFiles: inputFiles, - outputFiles: [outputSwiftPath] + outputFiles: [outputSwiftPath, outputMacrosSwiftPath] ) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift index 23fbae567..634720b89 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift @@ -103,6 +103,7 @@ extension BridgeJSCommandPlugin.Context { let generatedDirectory = target.directoryURL.appending(path: "Generated") let bridgeDtsPath = target.directoryURL.appending(path: "bridge-js.d.ts") + let bridgeGlobalDtsPath = target.directoryURL.appending(path: "bridge-js.global.d.ts") let tsconfigPath = context.package.directoryURL.appending(path: "tsconfig.json") // Unified generate command @@ -118,12 +119,16 @@ extension BridgeJSCommandPlugin.Context { options.verbose ? "true" : "false", ] - if FileManager.default.fileExists(atPath: bridgeDtsPath.path) { + let hasDts = FileManager.default.fileExists(atPath: bridgeDtsPath.path) + let hasGlobalDts = FileManager.default.fileExists(atPath: bridgeGlobalDtsPath.path) + if hasDts || hasGlobalDts { generateArguments.append(contentsOf: [ "--project", tsconfigPath.path, - bridgeDtsPath.path, ]) + if hasDts { + generateArguments.append(bridgeDtsPath.path) + } } generateArguments.append(