From ab19ba993e9dc48d49ca8b1e9d19ec428998cb5b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 23 Mar 2026 10:57:21 +0000 Subject: [PATCH 01/20] 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/20] 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/20] 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/20] `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/20] 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/20] 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/20] 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/20] 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/20] 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 cdb8013aa26c4d0ccdd5a798a1a47fdeea5ab30d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 17:04:08 +0000 Subject: [PATCH 10/20] Check more stack ABI types --- .../AsyncImportTests.swift | 37 ++ .../Generated/BridgeJS.Macros.swift | 12 + .../Generated/BridgeJS.swift | 514 ++++++++++++++++++ .../Generated/JavaScript/BridgeJS.json | 184 +++++++ .../JavaScript/AsyncImportTests.mjs | 61 +++ Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 8 + Tests/prelude.mjs | 34 +- 7 files changed, 818 insertions(+), 32 deletions(-) create mode 100644 Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift index 9b6cc0688..549ab8750 100644 --- a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -21,6 +21,43 @@ import JavaScriptKit try #expect(await jsAsyncRoundTripString(v) == v) } + // MARK: - Stack ABI types + + @Test(arguments: ["hello" as String?, nil, "🧑‍🧑‍🧒" as String?]) + func asyncRoundTripOptionalString(v: String?) async throws { + try #expect(await jsAsyncRoundTripOptionalString(v) == v) + } + + @Test(arguments: [42.0 as Double?, nil, 0.0 as Double?]) + func asyncRoundTripOptionalNumber(v: Double?) async throws { + try #expect(await jsAsyncRoundTripOptionalNumber(v) == v) + } + + @Test func asyncRoundTripBoolArray() async throws { + let values: [Bool] = [true, false, true] + try #expect(await jsAsyncRoundTripBoolArray(values) == values) + try #expect(await jsAsyncRoundTripBoolArray([]) == []) + } + + @Test func asyncRoundTripIntArray() async throws { + let values: [Double] = [1, 2, 3, 4, 5] + try #expect(await jsAsyncRoundTripIntArray(values) == values) + try #expect(await jsAsyncRoundTripIntArray([]) == []) + } + + @Test func asyncRoundTripStringArray() async throws { + let values = ["Hello", "World", "🎉"] + try #expect(await jsAsyncRoundTripStringArray(values) == values) + try #expect(await jsAsyncRoundTripStringArray([]) == []) + } + + @Test(arguments: [FeatureFlag.foo, .bar]) + func asyncRoundTripFeatureFlag(v: FeatureFlag) async throws { + try #expect(await jsAsyncRoundTripFeatureFlag(v) == v) + } + + // MARK: - Structured return type + @Test func fetchWeatherData() async throws { let weather = try await BridgeJSRuntimeTests.fetchWeatherData("London") #expect(try weather.temperature == 15.5) diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index b4e780bee..d62d96f08 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -62,6 +62,18 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSSetter func setHumidity(_ value: Double) throws(JSException) } +@JSFunction func jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional + +@JSFunction func jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional + +@JSFunction func jsAsyncRoundTripBoolArray(_ v: [Bool]) async throws(JSException) -> [Bool] + +@JSFunction func jsAsyncRoundTripIntArray(_ v: [Double]) async throws(JSException) -> [Double] + +@JSFunction func jsAsyncRoundTripStringArray(_ v: [String]) async throws(JSException) -> [String] + +@JSFunction func jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag + @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 b7d6b55c3..8afdd09a6 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -1674,6 +1674,68 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSqS #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ callback: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(callback, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestss11FeatureFlagO_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending FeatureFlag) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(callbackValue, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending FeatureFlag) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending FeatureFlag) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11FeatureFlagO_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending FeatureFlag) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(FeatureFlag.bridgeJSLiftParameter(param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11WeatherDataC_y_extern(_ callback: Int32, _ param0: Int32) -> Void @@ -1858,6 +1920,189 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSS #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [String]) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(callbackValue) + #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_20BridgeJSRuntimeTestssSaSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSS_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [String]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([String].bridgeJSLiftParameter()) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSb_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [Bool]) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(callbackValue) + #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_20BridgeJSRuntimeTestssSaSb_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSb_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [Bool]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([Bool].bridgeJSLiftParameter()) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ callback: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSaSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending [Double]) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let _ = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(callbackValue) + #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_20BridgeJSRuntimeTestssSaSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSaSd_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending [Double]) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure([Double].bridgeJSLiftParameter()) + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSb_y_extern(_ callback: Int32, _ param0: Int32) -> Void @@ -1980,6 +2225,129 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSd #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ callback: Int32, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(callback, param0IsSome, param0Bytes, param0Length) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSqSS_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Optional) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + param0.bridgeJSWithLoweredParameter { (param0IsSome, param0Bytes, param0Length) in + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(callbackValue, param0IsSome, param0Bytes, param0Length) + } + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Optional) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Optional) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSS_y(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Bytes: Int32, _ param0Length: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Optional) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Bytes, param0Length)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ callback: Int32, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { + return invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(callback, param0IsSome, param0Value) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestssSqSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Optional) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Value) = param0.bridgeJSLowerParameter() + invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(callbackValue, param0IsSome, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Optional) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Optional) -> Void) { + self.init( + makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestssSqSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Optional) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + #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 @@ -11742,6 +12110,152 @@ func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData return resolved } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripOptionalString") +fileprivate func bjs_jsAsyncRoundTripOptionalString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripOptionalString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripOptionalString(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_jsAsyncRoundTripOptionalString_extern(resolveRef, rejectRef, vIsSome, vBytes, vLength) +} + +func _$jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vIsSome, vBytes, vLength) in + bjs_jsAsyncRoundTripOptionalString(resolveRef, rejectRef, vIsSome, vBytes, vLength) + } + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripOptionalNumber") +fileprivate func bjs_jsAsyncRoundTripOptionalNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripOptionalNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripOptionalNumber(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + return bjs_jsAsyncRoundTripOptionalNumber_extern(resolveRef, rejectRef, vIsSome, vValue) +} + +func _$jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let (vIsSome, vValue) = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripOptionalNumber(resolveRef, rejectRef, vIsSome, vValue) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBoolArray") +fileprivate func bjs_jsAsyncRoundTripBoolArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripBoolArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripBoolArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_jsAsyncRoundTripBoolArray_extern(resolveRef, rejectRef) +} + +func _$jsAsyncRoundTripBoolArray(_ v: [Bool]) async throws(JSException) -> [Bool] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Bool]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripBoolArray(resolveRef, rejectRef) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripIntArray") +fileprivate func bjs_jsAsyncRoundTripIntArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripIntArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripIntArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_jsAsyncRoundTripIntArray_extern(resolveRef, rejectRef) +} + +func _$jsAsyncRoundTripIntArray(_ v: [Double]) async throws(JSException) -> [Double] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Double]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripIntArray(resolveRef, rejectRef) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripStringArray") +fileprivate func bjs_jsAsyncRoundTripStringArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripStringArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripStringArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_jsAsyncRoundTripStringArray_extern(resolveRef, rejectRef) +} + +func _$jsAsyncRoundTripStringArray(_ v: [String]) async throws(JSException) -> [String] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [String]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = v.bridgeJSLowerParameter() + bjs_jsAsyncRoundTripStringArray(resolveRef, rejectRef) + } + return resolved +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripFeatureFlag") +fileprivate func bjs_jsAsyncRoundTripFeatureFlag_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_jsAsyncRoundTripFeatureFlag_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_jsAsyncRoundTripFeatureFlag(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_jsAsyncRoundTripFeatureFlag_extern(resolveRef, rejectRef, vBytes, vLength) +} + +func _$jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending FeatureFlag) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_jsAsyncRoundTripFeatureFlag(resolveRef, rejectRef, vBytes, vLength) + } + } + return resolved +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__jsWeirdFunction") fileprivate func bjs__jsWeirdFunction_extern() -> Float64 diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index c8c25c492..cd3d72190 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -18434,6 +18434,190 @@ } } }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalString", + "parameters" : [ + { + "name" : "v", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalNumber", + "parameters" : [ + { + "name" : "v", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripBoolArray", + "parameters" : [ + { + "name" : "v", + "type" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripIntArray", + "parameters" : [ + { + "name" : "v", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripStringArray", + "parameters" : [ + { + "name" : "v", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripFeatureFlag", + "parameters" : [ + { + "name" : "v", + "type" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + } + ], + "returnType" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + }, { "effects" : { "isAsync" : false, diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs new file mode 100644 index 000000000..a189b92a8 --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs @@ -0,0 +1,61 @@ +// @ts-check + +import assert from 'node:assert'; + +/** + * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports} + */ +export function getImports(importsContext) { + return { + jsAsyncRoundTripVoid: () => { + return Promise.resolve(); + }, + jsAsyncRoundTripNumber: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripBool: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripString: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripOptionalString: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripOptionalNumber: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripBoolArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripIntArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripStringArray: (v) => { + return Promise.resolve(v); + }, + jsAsyncRoundTripFeatureFlag: (v) => { + return Promise.resolve(v); + }, + fetchWeatherData: (city) => { + return Promise.resolve({ + temperature: city === "London" ? 15.5 : 25.0, + description: city === "London" ? "Cloudy" : "Sunny", + humidity: city === "London" ? 80 : 40, + }); + }, + runAsyncWorks: async () => { + const exports = importsContext.getExports(); + if (!exports) { + throw new Error("No exports!?"); + } + await runAsyncWorksTests(exports); + return; + }, + }; +} + +/** @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ +async function runAsyncWorksTests(exports) { + await exports.asyncRoundTripVoid(); +} diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 635a783b8..a97c167e6 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -40,6 +40,14 @@ export interface WeatherData { } export function fetchWeatherData(city: string): Promise; +// Async Stack ABI type round-trips +export function jsAsyncRoundTripOptionalString(v: string | null): Promise; +export function jsAsyncRoundTripOptionalNumber(v: number | null): Promise; +export function jsAsyncRoundTripBoolArray(v: boolean[]): Promise; +export function jsAsyncRoundTripIntArray(v: number[]): Promise; +export function jsAsyncRoundTripStringArray(v: string[]): Promise; +export function jsAsyncRoundTripFeatureFlag(v: FeatureFlag): Promise; + // jsName tests export function $jsWeirdFunction(): number; diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 8c652bc5c..4cd766186 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -13,6 +13,7 @@ import { getImports as getDictionarySupportImports } from './BridgeJSRuntimeTest import { getImports as getDefaultArgumentImports } from './BridgeJSRuntimeTests/JavaScript/DefaultArgumentTests.mjs'; import { getImports as getJSClassSupportImports, JSClassWithArrayMembers } from './BridgeJSRuntimeTests/JavaScript/JSClassSupportTests.mjs'; import { getImports as getIntegerTypesSupportImports } from './BridgeJSRuntimeTests/JavaScript/IntegerTypesSupportTests.mjs'; +import { getImports as getAsyncImportImports } from './BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ export async function setupOptions(options, context) { @@ -119,33 +120,7 @@ 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(); - }, - "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) { - throw new Error("No exports!?"); - } - BridgeJSRuntimeTests_runAsyncWorks(exports); - return; - }, + ...getAsyncImportImports(importsContext), jsTranslatePoint: (point, dx, dy) => { return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) }; }, @@ -1028,11 +1003,6 @@ function testStructSupport(exports) { assert.equal(fooContainerResult2.optionalFoo, null); } -/** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ -async function BridgeJSRuntimeTests_runAsyncWorks(exports) { - await exports.asyncRoundTripVoid(); -} - /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ function BridgeJSRuntimeTests_runJsStructWorks(exports) { testStructSupport(exports); From 695a71526ea3f439b82c06c4db8f5194080aa8ec Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 19:20:35 +0000 Subject: [PATCH 11/20] Add support for `async` in `@JSFunction` --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 2 +- .../Sources/BridgeJSCore/ImportTS.swift | 7 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 38 +- .../Inputs/MacroSwift/AsyncStaticImport.swift | 4 + .../AsyncStaticImport.json | 67 +++ .../AsyncStaticImport.swift | 227 ++++++++++ .../BridgeJSLinkTests/AsyncStaticImport.d.ts | 23 + .../BridgeJSLinkTests/AsyncStaticImport.js | 402 ++++++++++++++++++ 8 files changed, 755 insertions(+), 15 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index fcf71611b..d67c717f1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -232,7 +232,7 @@ public struct ClosureCodegen { } } for type in file.types { - for method in type.methods where method.effects.isAsync { + for method in (type.methods + type.staticMethods) where method.effects.isAsync { closureSignatures.insert( ClosureSignature( parameters: [.jsValue], diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index d709dd190..1d5f40c12 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -477,12 +477,15 @@ public struct ImportTS { func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { let abiName = method.abiName(context: type, operation: "static") - 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: abiName, returnType: abiReturnType) + if method.effects.isAsync { + builder.prependClosureCallbackParams() + } 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 573693e4f..e1410126b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -677,7 +677,7 @@ public struct BridgeJSLink { } } for type in file.types { - for method in type.methods where method.effects.isAsync { + for method in (type.methods + type.staticMethods) where method.effects.isAsync { closureSignatures.insert( ClosureSignature( parameters: [.jsValue], @@ -2397,6 +2397,13 @@ extension BridgeJSLink { ) } + /// Generates an async static method call with resolve/reject closure refs. + func callAsyncStaticMethod(on objectExpr: String, name: String) { + let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) + let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" + body.write("\(callExpr).then(resolve, reject);") + } + func callPropertyGetter(name: String, returnType: BridgeType) throws -> String? { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) @@ -3345,7 +3352,7 @@ extension BridgeJSLink { for method in type.staticMethods { let methodName = method.jsName ?? method.name let signature = - "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: Effects(isAsync: false, isThrows: false)));" + "\(renderTSPropertyName(methodName))\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" dtsPrinter.write(signature) } } @@ -3429,16 +3436,23 @@ extension BridgeJSLink { objectExpr: importRootExpr, propertyName: context.jsName ?? context.name ) - let returnExpr = try thunkBuilder.callStaticMethod( - on: constructorExpr, - name: method.jsName ?? method.name, - returnType: method.returnType - ) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName(context: context, operation: "static"), - returnExpr: returnExpr, - returnType: method.returnType - ) + + let funcLines: [String] + if method.effects.isAsync { + thunkBuilder.callAsyncStaticMethod(on: constructorExpr, name: method.jsName ?? method.name) + funcLines = thunkBuilder.renderAsyncFunction(name: method.abiName(context: context, operation: "static")) + } else { + let returnExpr = try thunkBuilder.callStaticMethod( + on: constructorExpr, + name: method.jsName ?? method.name, + returnType: method.returnType + ) + funcLines = thunkBuilder.renderFunction( + name: method.abiName(context: context, operation: "static"), + returnExpr: returnExpr, + returnType: method.returnType + ) + } return (funcLines, []) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift new file mode 100644 index 000000000..1afd758bf --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/AsyncStaticImport.swift @@ -0,0 +1,4 @@ +@JSClass struct AsyncBox { + @JSFunction static func asyncStaticRoundTrip(_ v: Double) async throws(JSException) -> Double + @JSFunction static func asyncStaticVoid() async throws(JSException) +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json new file mode 100644 index 000000000..972a532c6 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json @@ -0,0 +1,67 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "AsyncBox", + "setters" : [ + + ], + "staticMethods" : [ + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncStaticRoundTrip", + "parameters" : [ + { + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "asyncStaticVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + } + ] + } + ] + } + ] + }, + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift new file mode 100644 index 000000000..ee7dc73e7 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.swift @@ -0,0 +1,227 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModules7JSValueV_y") +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModules7JSValueV_y(_ callback: Int32, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModules7JSValueV_y_extern(callback, param0Kind, param0Payload1, param0Payload2) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModules7JSValueV_y") +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModules7JSValueV_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModules7JSValueV_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending JSValue) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0Kind, param0Payload1, param0Payload2) = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModules7JSValueV_y(callbackValue, param0Kind, param0Payload1, param0Payload2) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending JSValue) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending JSValue) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModules7JSValueV_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModules7JSValueV_y") +public func _invoke_swift_closure_TestModule_10TestModules7JSValueV_y(_ boxPtr: UnsafeMutableRawPointer, _ param0Kind: Int32, _ param0Payload1: Int32, _ param0Payload2: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending JSValue) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSValue.bridgeJSLiftParameter(param0Kind, param0Payload1, param0Payload2)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModulesSd_y") +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y_extern(_ callback: Int32, _ param0: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModulesSd_y(_ callback: Int32, _ param0: Float64) -> Void { + return invoke_js_callback_TestModule_10TestModulesSd_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModulesSd_y") +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModulesSd_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModulesSd_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModulesSd_y { + static func bridgeJSLift(_ callbackId: Int32) -> (sending Double) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModulesSd_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (sending Double) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (sending Double) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModulesSd_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModulesSd_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModulesSd_y") +public func _invoke_swift_closure_TestModule_10TestModulesSd_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Float64) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(sending Double) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(Double.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuley_y") +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModuley_y_extern(_ callback: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuley_y(_ callback: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModuley_y_extern(callback) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModuley_y") +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModuley_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModuley_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModuley_y { + static func bridgeJSLift(_ callbackId: Int32) -> () -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModuley_y(callbackValue) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == () -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping () -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModuley_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModuley_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModuley_y") +public func _invoke_swift_closure_TestModule_10TestModuley_y(_ boxPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<() -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure() + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_AsyncBox_asyncStaticRoundTrip_static") +fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void +#else +fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncBox_asyncStaticRoundTrip_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_AsyncBox_asyncStaticRoundTrip_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_AsyncBox_asyncStaticVoid_static") +fileprivate func bjs_AsyncBox_asyncStaticVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncBox_asyncStaticVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncBox_asyncStaticVoid_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncBox_asyncStaticVoid_static_extern(resolveRef, rejectRef) +} + +func _$AsyncBox_asyncStaticRoundTrip(_ v: Double) async throws(JSException) -> Double { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncBox_asyncStaticRoundTrip_static(resolveRef, rejectRef, vValue) + } + return resolved +} + +func _$AsyncBox_asyncStaticVoid() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_AsyncBox_asyncStaticVoid_static(resolveRef, rejectRef) + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts new file mode 100644 index 000000000..491a66795 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.d.ts @@ -0,0 +1,23 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export interface AsyncBox { +} +export type Exports = { +} +export type Imports = { + AsyncBox: { + asyncStaticRoundTrip(v: number): Promise; + asyncStaticVoid(): Promise; + } +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js new file mode 100644 index 000000000..7fd6a0d6b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js @@ -0,0 +1,402 @@ +// 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; + function __bjs_jsValueLower(value) { + let kind; + let payload1; + let payload2; + if (value === null) { + kind = 4; + payload1 = 0; + payload2 = 0; + } else { + switch (typeof value) { + case "boolean": + kind = 0; + payload1 = value ? 1 : 0; + payload2 = 0; + break; + case "number": + kind = 2; + payload1 = 0; + payload2 = value; + break; + case "string": + kind = 1; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "undefined": + kind = 5; + payload1 = 0; + payload2 = 0; + break; + case "object": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "function": + kind = 3; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "symbol": + kind = 7; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + case "bigint": + kind = 8; + payload1 = swift.memory.retain(value); + payload2 = 0; + break; + default: + throw new TypeError("Unsupported JSValue type"); + } + } + return [kind, payload1, payload2]; + } + function __bjs_jsValueLift(kind, payload1, payload2) { + let jsValue; + switch (kind) { + case 0: + jsValue = payload1 !== 0; + break; + case 1: + jsValue = swift.memory.getObject(payload1); + break; + case 2: + jsValue = payload2; + break; + case 3: + jsValue = swift.memory.getObject(payload1); + break; + case 4: + jsValue = null; + break; + case 5: + jsValue = undefined; + break; + case 7: + jsValue = swift.memory.getObject(payload1); + break; + case 8: + jsValue = swift.memory.getObject(payload1); + break; + default: + throw new TypeError("Unsupported JSValue kind " + kind); + } + return jsValue; + } + + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModules7JSValueV_y"] = function(callbackId, param0Kind, param0Payload1, param0Payload2) { + try { + const callback = swift.memory.getObject(callbackId); + const jsValue = __bjs_jsValueLift(param0Kind, param0Payload1, param0Payload2); + callback(jsValue); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModules7JSValueV_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModules7JSValueV_y = function(param0) { + const [param0Kind, param0Payload1, param0Payload2] = __bjs_jsValueLower(param0); + instance.exports.invoke_swift_closure_TestModule_10TestModules7JSValueV_y(boxPtr, param0Kind, param0Payload1, param0Payload2); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModules7JSValueV_y); + } + bjs["invoke_js_callback_TestModule_10TestModulesSd_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(param0); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModulesSd_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModulesSd_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModulesSd_y(boxPtr, param0); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModulesSd_y); + } + bjs["invoke_js_callback_TestModule_10TestModuley_y"] = function(callbackId) { + try { + const callback = swift.memory.getObject(callbackId); + callback(); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModuley_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModuley_y = function() { + instance.exports.invoke_swift_closure_TestModule_10TestModuley_y(boxPtr); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuley_y); + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_AsyncBox_asyncStaticRoundTrip_static"] = function bjs_AsyncBox_asyncStaticRoundTrip_static(resolveRef, rejectRef, v) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.AsyncBox.asyncStaticRoundTrip(v).then(resolve, reject); + } + TestModule["bjs_AsyncBox_asyncStaticVoid_static"] = function bjs_AsyncBox_asyncStaticVoid_static(resolveRef, rejectRef) { + const resolve = swift.memory.getObject(resolveRef); + const reject = swift.memory.getObject(rejectRef); + imports.AsyncBox.asyncStaticVoid().then(resolve, reject); + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file From 67ff8e617230690872c30abc1498ecefd647ce71 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 19:46:23 +0000 Subject: [PATCH 12/20] Use namespaced import --- .../AsyncImportTests.swift | 44 +- .../Generated/BridgeJS.Macros.swift | 31 - .../Generated/BridgeJS.swift | 695 ++++++--------- .../Generated/JavaScript/BridgeJS.json | 802 ++++++++---------- .../JavaScript/AsyncImportTests.mjs | 12 +- Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 16 - Tests/prelude.mjs | 12 +- 7 files changed, 650 insertions(+), 962 deletions(-) diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift index 549ab8750..e184b3bed 100644 --- a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -1,70 +1,84 @@ import Testing import JavaScriptKit +@JSClass struct AsyncImportImports { + @JSFunction static func jsAsyncRoundTripVoid() async throws(JSException) + @JSFunction static func jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double + @JSFunction static func jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool + @JSFunction static func jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String + @JSFunction static func jsAsyncRoundTripOptionalString(_ v: String?) async throws(JSException) -> String? + @JSFunction static func jsAsyncRoundTripOptionalNumber(_ v: Double?) async throws(JSException) -> Double? + @JSFunction static func jsAsyncRoundTripBoolArray(_ values: [Bool]) async throws(JSException) -> [Bool] + @JSFunction static func jsAsyncRoundTripIntArray(_ values: [Double]) async throws(JSException) -> [Double] + @JSFunction static func jsAsyncRoundTripStringArray(_ values: [String]) async throws(JSException) -> [String] + @JSFunction static func jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag + @JSFunction static func fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData +} + @Suite struct AsyncImportTests { @Test func asyncRoundTripVoid() async throws { - try await jsAsyncRoundTripVoid() + try await AsyncImportImports.jsAsyncRoundTripVoid() } @Test(arguments: [0.0, 1.0, -1.0, Double.pi, Double.infinity]) func asyncRoundTripNumber(v: Double) async throws { - try #expect(await jsAsyncRoundTripNumber(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripNumber(v) == v) } @Test(arguments: [true, false]) func asyncRoundTripBool(v: Bool) async throws { - try #expect(await jsAsyncRoundTripBool(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripBool(v) == v) } @Test(arguments: ["", "Hello, world!", "🧑‍🧑‍🧒"]) func asyncRoundTripString(v: String) async throws { - try #expect(await jsAsyncRoundTripString(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripString(v) == v) } // MARK: - Stack ABI types @Test(arguments: ["hello" as String?, nil, "🧑‍🧑‍🧒" as String?]) func asyncRoundTripOptionalString(v: String?) async throws { - try #expect(await jsAsyncRoundTripOptionalString(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripOptionalString(v) == v) } @Test(arguments: [42.0 as Double?, nil, 0.0 as Double?]) func asyncRoundTripOptionalNumber(v: Double?) async throws { - try #expect(await jsAsyncRoundTripOptionalNumber(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripOptionalNumber(v) == v) } @Test func asyncRoundTripBoolArray() async throws { let values: [Bool] = [true, false, true] - try #expect(await jsAsyncRoundTripBoolArray(values) == values) - try #expect(await jsAsyncRoundTripBoolArray([]) == []) + try #expect(await AsyncImportImports.jsAsyncRoundTripBoolArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripBoolArray([]) == []) } @Test func asyncRoundTripIntArray() async throws { let values: [Double] = [1, 2, 3, 4, 5] - try #expect(await jsAsyncRoundTripIntArray(values) == values) - try #expect(await jsAsyncRoundTripIntArray([]) == []) + try #expect(await AsyncImportImports.jsAsyncRoundTripIntArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripIntArray([]) == []) } @Test func asyncRoundTripStringArray() async throws { let values = ["Hello", "World", "🎉"] - try #expect(await jsAsyncRoundTripStringArray(values) == values) - try #expect(await jsAsyncRoundTripStringArray([]) == []) + try #expect(await AsyncImportImports.jsAsyncRoundTripStringArray(values) == values) + try #expect(await AsyncImportImports.jsAsyncRoundTripStringArray([]) == []) } @Test(arguments: [FeatureFlag.foo, .bar]) func asyncRoundTripFeatureFlag(v: FeatureFlag) async throws { - try #expect(await jsAsyncRoundTripFeatureFlag(v) == v) + try #expect(await AsyncImportImports.jsAsyncRoundTripFeatureFlag(v) == v) } // MARK: - Structured return type @Test func fetchWeatherData() async throws { - let weather = try await BridgeJSRuntimeTests.fetchWeatherData("London") + let weather = try await AsyncImportImports.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") + let weather2 = try await AsyncImportImports.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 d62d96f08..b2d0fb209 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -43,37 +43,6 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @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 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 func jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional - -@JSFunction func jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional - -@JSFunction func jsAsyncRoundTripBoolArray(_ v: [Bool]) async throws(JSException) -> [Bool] - -@JSFunction func jsAsyncRoundTripIntArray(_ v: [Double]) async throws(JSException) -> [Double] - -@JSFunction func jsAsyncRoundTripStringArray(_ v: [String]) async throws(JSException) -> [String] - -@JSFunction func jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag - @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 8afdd09a6..06f4c8c00 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -1736,67 +1736,6 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11 #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 @@ -11163,6 +11102,247 @@ func _$ArraySupportImports_runJsArraySupportTests() throws(JSException) -> Void } } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripVoid_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripVoid_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripVoid_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripNumber_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripNumber_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripNumber_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripBool_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBool_static(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripBool_static_extern(resolveRef, rejectRef, v) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripString_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripString_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripString_static_extern(resolveRef, rejectRef, vBytes, vLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static_extern(resolveRef, rejectRef, vIsSome, vBytes, vLength) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static_extern(resolveRef, rejectRef, vIsSome, vValue) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static_extern(resolveRef, rejectRef) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static") +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void +#else +fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { + return bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static_extern(resolveRef, rejectRef, vBytes, vLength) +} + +func _$AsyncImportImports_jsAsyncRoundTripVoid() async throws(JSException) -> Void { + try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<() -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + bjs_AsyncImportImports_jsAsyncRoundTripVoid_static(resolveRef, rejectRef) + } +} + +func _$AsyncImportImports_jsAsyncRoundTripNumber(_ v: Double) async throws(JSException) -> Double { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Double) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripNumber_static(resolveRef, rejectRef, vValue) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripBool(_ v: Bool) async throws(JSException) -> Bool { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Bool) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let vValue = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripBool_static(resolveRef, rejectRef, vValue) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripString(_ v: String) async throws(JSException) -> String { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending String) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripString_static(resolveRef, rejectRef, vBytes, vLength) + } + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vIsSome, vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripOptionalString_static(resolveRef, rejectRef, vIsSome, vBytes, vLength) + } + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending Optional) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let (vIsSome, vValue) = v.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripOptionalNumber_static(resolveRef, rejectRef, vIsSome, vValue) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripBoolArray(_ values: [Bool]) async throws(JSException) -> [Bool] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Bool]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripBoolArray_static(resolveRef, rejectRef) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripIntArray(_ values: [Double]) async throws(JSException) -> [Double] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [Double]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripIntArray_static(resolveRef, rejectRef) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripStringArray(_ values: [String]) async throws(JSException) -> [String] { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending [String]) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + let _ = values.bridgeJSLowerParameter() + bjs_AsyncImportImports_jsAsyncRoundTripStringArray_static(resolveRef, rejectRef) + } + return resolved +} + +func _$AsyncImportImports_jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending FeatureFlag) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + v.bridgeJSWithLoweredParameter { (vBytes, vLength) in + bjs_AsyncImportImports_jsAsyncRoundTripFeatureFlag_static(resolveRef, rejectRef, vBytes, vLength) + } + } + return resolved +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ClosureSupportImports_jsApplyVoid_static") fileprivate func bjs_ClosureSupportImports_jsApplyVoid_static_extern(_ callback: Int32) -> Void @@ -11990,272 +12170,6 @@ func _$runAsyncWorks() async throws(JSException) -> Void { } } -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripVoid") -fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripVoid_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@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(makeResolveClosure: { - JSTypedClosure<() -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - bjs_jsAsyncRoundTripVoid(resolveRef, rejectRef) - } -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripNumber") -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Float64) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@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(makeResolveClosure: { - JSTypedClosure<(sending Double) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let vValue = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripNumber(resolveRef, rejectRef, vValue) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBool") -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripBool_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ v: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@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(makeResolveClosure: { - JSTypedClosure<(sending Bool) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let vValue = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripBool(resolveRef, rejectRef, vValue) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripString") -fileprivate func bjs_jsAsyncRoundTripString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void -#else -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(_ 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(makeResolveClosure: { - JSTypedClosure<(sending String) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - bjs_jsAsyncRoundTripString(resolveRef, rejectRef, vBytes, vLength) - } - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") -fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void -#else -fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_fetchWeatherData(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { - return bjs_fetchWeatherData_extern(resolveRef, rejectRef, cityBytes, cityLength) -} - -func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending WeatherData) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in - bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) - } - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripOptionalString") -fileprivate func bjs_jsAsyncRoundTripOptionalString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripOptionalString_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripOptionalString(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - return bjs_jsAsyncRoundTripOptionalString_extern(resolveRef, rejectRef, vIsSome, vBytes, vLength) -} - -func _$jsAsyncRoundTripOptionalString(_ v: Optional) async throws(JSException) -> Optional { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending Optional) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - v.bridgeJSWithLoweredParameter { (vIsSome, vBytes, vLength) in - bjs_jsAsyncRoundTripOptionalString(resolveRef, rejectRef, vIsSome, vBytes, vLength) - } - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripOptionalNumber") -fileprivate func bjs_jsAsyncRoundTripOptionalNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripOptionalNumber_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripOptionalNumber(_ resolveRef: Int32, _ rejectRef: Int32, _ vIsSome: Int32, _ vValue: Float64) -> Void { - return bjs_jsAsyncRoundTripOptionalNumber_extern(resolveRef, rejectRef, vIsSome, vValue) -} - -func _$jsAsyncRoundTripOptionalNumber(_ v: Optional) async throws(JSException) -> Optional { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending Optional) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let (vIsSome, vValue) = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripOptionalNumber(resolveRef, rejectRef, vIsSome, vValue) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripBoolArray") -fileprivate func bjs_jsAsyncRoundTripBoolArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripBoolArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripBoolArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - return bjs_jsAsyncRoundTripBoolArray_extern(resolveRef, rejectRef) -} - -func _$jsAsyncRoundTripBoolArray(_ v: [Bool]) async throws(JSException) -> [Bool] { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending [Bool]) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let _ = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripBoolArray(resolveRef, rejectRef) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripIntArray") -fileprivate func bjs_jsAsyncRoundTripIntArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripIntArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripIntArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - return bjs_jsAsyncRoundTripIntArray_extern(resolveRef, rejectRef) -} - -func _$jsAsyncRoundTripIntArray(_ v: [Double]) async throws(JSException) -> [Double] { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending [Double]) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let _ = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripIntArray(resolveRef, rejectRef) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripStringArray") -fileprivate func bjs_jsAsyncRoundTripStringArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripStringArray_extern(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripStringArray(_ resolveRef: Int32, _ rejectRef: Int32) -> Void { - return bjs_jsAsyncRoundTripStringArray_extern(resolveRef, rejectRef) -} - -func _$jsAsyncRoundTripStringArray(_ v: [String]) async throws(JSException) -> [String] { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending [String]) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - let _ = v.bridgeJSLowerParameter() - bjs_jsAsyncRoundTripStringArray(resolveRef, rejectRef) - } - return resolved -} - -#if arch(wasm32) -@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsAsyncRoundTripFeatureFlag") -fileprivate func bjs_jsAsyncRoundTripFeatureFlag_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void -#else -fileprivate func bjs_jsAsyncRoundTripFeatureFlag_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - fatalError("Only available on WebAssembly") -} -#endif -@inline(never) fileprivate func bjs_jsAsyncRoundTripFeatureFlag(_ resolveRef: Int32, _ rejectRef: Int32, _ vBytes: Int32, _ vLength: Int32) -> Void { - return bjs_jsAsyncRoundTripFeatureFlag_extern(resolveRef, rejectRef, vBytes, vLength) -} - -func _$jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag { - let resolved = try await _bjs_awaitPromise(makeResolveClosure: { - JSTypedClosure<(sending FeatureFlag) -> Void>($0) - }, makeRejectClosure: { - JSTypedClosure<(sending JSValue) -> Void>($0) - }) { resolveRef, rejectRef in - v.bridgeJSWithLoweredParameter { (vBytes, vLength) in - bjs_jsAsyncRoundTripFeatureFlag(resolveRef, rejectRef, vBytes, vLength) - } - } - return resolved -} - #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__jsWeirdFunction") fileprivate func bjs__jsWeirdFunction_extern() -> Float64 @@ -12434,133 +12348,6 @@ 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 cd3d72190..384b9a6c0 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -16988,6 +16988,296 @@ { "functions" : [ + ], + "types" : [ + { + "getters" : [ + + ], + "methods" : [ + + ], + "name" : "AsyncImportImports", + "setters" : [ + + ], + "staticMethods" : [ + { + "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" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalString", + "parameters" : [ + { + "name" : "v", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripOptionalNumber", + "parameters" : [ + { + "name" : "v", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripBoolArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripIntArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripStringArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "jsAsyncRoundTripFeatureFlag", + "parameters" : [ + { + "name" : "v", + "type" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + } + ], + "returnType" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" + } + } + } + ] + } + ] + }, + { + "functions" : [ + ], "types" : [ { @@ -18041,298 +18331,57 @@ ] } ] - }, - { - "functions" : [ - - ], - "types" : [ - { - "constructor" : { - "parameters" : [ - { - "name" : "value", - "type" : { - "string" : { - - } - } - } - ] - }, - "getters" : [ - { - "name" : "value", - "type" : { - "string" : { - - } - } - } - ], - "methods" : [ - - ], - "name" : "Foo", - "setters" : [ - - ], - "staticMethods" : [ - - ] - } - ] - }, - { - "functions" : [ - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsRoundTripVoid", - "parameters" : [ - - ], - "returnType" : { - "void" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsRoundTripNumber", - "parameters" : [ - { - "name" : "v", - "type" : { - "double" : { - - } - } - } - ], - "returnType" : { - "double" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsRoundTripBool", - "parameters" : [ - { - "name" : "v", - "type" : { - "bool" : { - - } - } - } - ], - "returnType" : { - "bool" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsRoundTripString", - "parameters" : [ - { - "name" : "v", - "type" : { - "string" : { - - } - } - } - ], - "returnType" : { - "string" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsRoundTripJSValue", - "parameters" : [ - { - "name" : "v", - "type" : { - "jsValue" : { - - } - } - } - ], - "returnType" : { - "jsValue" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsThrowOrVoid", - "parameters" : [ - { - "name" : "shouldThrow", - "type" : { - "bool" : { - - } - } - } - ], - "returnType" : { - "void" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsThrowOrNumber", - "parameters" : [ - { - "name" : "shouldThrow", - "type" : { - "bool" : { - - } - } - } - ], - "returnType" : { - "double" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsThrowOrBool", - "parameters" : [ - { - "name" : "shouldThrow", - "type" : { - "bool" : { - - } - } - } - ], - "returnType" : { - "bool" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true - }, - "name" : "jsThrowOrString", - "parameters" : [ - { - "name" : "shouldThrow", - "type" : { - "bool" : { + }, + { + "functions" : [ + + ], + "types" : [ + { + "constructor" : { + "parameters" : [ + { + "name" : "value", + "type" : { + "string" : { + } } } - } - ], - "returnType" : { - "string" : { - - } - } - }, - { - "effects" : { - "isAsync" : false, - "isStatic" : false, - "isThrows" : true + ] }, - "name" : "jsRoundTripFeatureFlag", - "parameters" : [ + "getters" : [ { - "name" : "flag", + "name" : "value", "type" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "String" + "string" : { + } } } ], - "returnType" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "String" - } - } - }, - { - "effects" : { - "isAsync" : true, - "isStatic" : false, - "isThrows" : true - }, - "name" : "runAsyncWorks", - "parameters" : [ + "methods" : [ ], - "returnType" : { - "void" : { + "name" : "Foo", + "setters" : [ - } - } - }, + ], + "staticMethods" : [ + + ] + } + ] + }, + { + "functions" : [ { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripVoid", + "name" : "jsRoundTripVoid", "parameters" : [ ], @@ -18344,11 +18393,11 @@ }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripNumber", + "name" : "jsRoundTripNumber", "parameters" : [ { "name" : "v", @@ -18367,11 +18416,11 @@ }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripBool", + "name" : "jsRoundTripBool", "parameters" : [ { "name" : "v", @@ -18390,11 +18439,11 @@ }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripString", + "name" : "jsRoundTripString", "parameters" : [ { "name" : "v", @@ -18413,183 +18462,141 @@ }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "fetchWeatherData", + "name" : "jsRoundTripJSValue", "parameters" : [ { - "name" : "city", + "name" : "v", "type" : { - "string" : { + "jsValue" : { } } } ], "returnType" : { - "jsObject" : { - "_0" : "WeatherData" + "jsValue" : { + } } }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripOptionalString", + "name" : "jsThrowOrVoid", "parameters" : [ { - "name" : "v", + "name" : "shouldThrow", "type" : { - "nullable" : { - "_0" : { - "string" : { + "bool" : { - } - }, - "_1" : "null" } } } ], "returnType" : { - "nullable" : { - "_0" : { - "string" : { + "void" : { - } - }, - "_1" : "null" } } }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripOptionalNumber", + "name" : "jsThrowOrNumber", "parameters" : [ { - "name" : "v", + "name" : "shouldThrow", "type" : { - "nullable" : { - "_0" : { - "double" : { + "bool" : { - } - }, - "_1" : "null" } } } ], "returnType" : { - "nullable" : { - "_0" : { - "double" : { + "double" : { - } - }, - "_1" : "null" } } }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripBoolArray", + "name" : "jsThrowOrBool", "parameters" : [ { - "name" : "v", + "name" : "shouldThrow", "type" : { - "array" : { - "_0" : { - "bool" : { + "bool" : { - } - } } } } ], "returnType" : { - "array" : { - "_0" : { - "bool" : { + "bool" : { - } - } } } }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripIntArray", + "name" : "jsThrowOrString", "parameters" : [ { - "name" : "v", + "name" : "shouldThrow", "type" : { - "array" : { - "_0" : { - "double" : { + "bool" : { - } - } } } } ], "returnType" : { - "array" : { - "_0" : { - "double" : { + "string" : { - } - } } } }, { "effects" : { - "isAsync" : true, + "isAsync" : false, "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripStringArray", + "name" : "jsRoundTripFeatureFlag", "parameters" : [ { - "name" : "v", + "name" : "flag", "type" : { - "array" : { - "_0" : { - "string" : { - - } - } + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" } } } ], "returnType" : { - "array" : { - "_0" : { - "string" : { - - } - } + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "String" } } }, @@ -18599,22 +18606,13 @@ "isStatic" : false, "isThrows" : true }, - "name" : "jsAsyncRoundTripFeatureFlag", + "name" : "runAsyncWorks", "parameters" : [ - { - "name" : "v", - "type" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "String" - } - } - } + ], "returnType" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "String" + "void" : { + } } }, @@ -18768,70 +18766,6 @@ ] }, - { - "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/JavaScript/AsyncImportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs index a189b92a8..55f3e4ce0 100644 --- a/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs +++ b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs @@ -3,7 +3,7 @@ import assert from 'node:assert'; /** - * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports} + * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports["AsyncImportImports"]} */ export function getImports(importsContext) { return { @@ -44,18 +44,10 @@ export function getImports(importsContext) { humidity: city === "London" ? 80 : 40, }); }, - runAsyncWorks: async () => { - const exports = importsContext.getExports(); - if (!exports) { - throw new Error("No exports!?"); - } - await runAsyncWorksTests(exports); - return; - }, }; } /** @param {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ -async function runAsyncWorksTests(exports) { +export async function runAsyncWorksTests(exports) { await exports.asyncRoundTripVoid(); } diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index a97c167e6..9e1bd2c61 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -26,27 +26,11 @@ 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; - -// Async fetch-like test with structured return type export interface WeatherData { temperature: number; description: string; humidity: number; } -export function fetchWeatherData(city: string): Promise; - -// Async Stack ABI type round-trips -export function jsAsyncRoundTripOptionalString(v: string | null): Promise; -export function jsAsyncRoundTripOptionalNumber(v: number | null): Promise; -export function jsAsyncRoundTripBoolArray(v: boolean[]): Promise; -export function jsAsyncRoundTripIntArray(v: number[]): Promise; -export function jsAsyncRoundTripStringArray(v: string[]): Promise; -export function jsAsyncRoundTripFeatureFlag(v: FeatureFlag): Promise; // jsName tests export function $jsWeirdFunction(): number; diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 4cd766186..9ddf013af 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -13,7 +13,7 @@ import { getImports as getDictionarySupportImports } from './BridgeJSRuntimeTest import { getImports as getDefaultArgumentImports } from './BridgeJSRuntimeTests/JavaScript/DefaultArgumentTests.mjs'; import { getImports as getJSClassSupportImports, JSClassWithArrayMembers } from './BridgeJSRuntimeTests/JavaScript/JSClassSupportTests.mjs'; import { getImports as getIntegerTypesSupportImports } from './BridgeJSRuntimeTests/JavaScript/IntegerTypesSupportTests.mjs'; -import { getImports as getAsyncImportImports } from './BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs'; +import { getImports as getAsyncImportImports, runAsyncWorksTests } from './BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ export async function setupOptions(options, context) { @@ -120,7 +120,15 @@ export async function setupOptions(options, context) { }, StaticBox, Foo: ImportedFoo, - ...getAsyncImportImports(importsContext), + runAsyncWorks: async () => { + const exports = importsContext.getExports(); + if (!exports) { + throw new Error("No exports!?"); + } + await runAsyncWorksTests(exports); + return; + }, + AsyncImportImports: getAsyncImportImports(importsContext), jsTranslatePoint: (point, dx, dy) => { return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) }; }, From ae831f1e4d2f00a9c60eaf57e1fda7e207bd33ee Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 19:47:56 +0000 Subject: [PATCH 13/20] Fix missing `fetchWeatherData` --- Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 9e1bd2c61..9fef391c1 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -31,6 +31,7 @@ export interface WeatherData { description: string; humidity: number; } +export function fetchWeatherData(city: string): Promise; // jsName tests export function $jsWeirdFunction(): number; From a250ab319001517167c65895b06c7faa85a3e2c4 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 19:49:12 +0000 Subject: [PATCH 14/20] Bring back `fetchWeatherData` --- Tests/BridgeJSRuntimeTests/AsyncImportTests.swift | 5 ++--- Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs | 7 ------- Tests/prelude.mjs | 7 +++++++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift index e184b3bed..041f251f4 100644 --- a/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift +++ b/Tests/BridgeJSRuntimeTests/AsyncImportTests.swift @@ -12,7 +12,6 @@ import JavaScriptKit @JSFunction static func jsAsyncRoundTripIntArray(_ values: [Double]) async throws(JSException) -> [Double] @JSFunction static func jsAsyncRoundTripStringArray(_ values: [String]) async throws(JSException) -> [String] @JSFunction static func jsAsyncRoundTripFeatureFlag(_ v: FeatureFlag) async throws(JSException) -> FeatureFlag - @JSFunction static func fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData } @Suite struct AsyncImportTests { @@ -73,12 +72,12 @@ import JavaScriptKit // MARK: - Structured return type @Test func fetchWeatherData() async throws { - let weather = try await AsyncImportImports.fetchWeatherData("London") + 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 AsyncImportImports.fetchWeatherData("Tokyo") + 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/JavaScript/AsyncImportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs index 55f3e4ce0..f64531d4a 100644 --- a/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs +++ b/Tests/BridgeJSRuntimeTests/JavaScript/AsyncImportTests.mjs @@ -37,13 +37,6 @@ export function getImports(importsContext) { jsAsyncRoundTripFeatureFlag: (v) => { return Promise.resolve(v); }, - fetchWeatherData: (city) => { - return Promise.resolve({ - temperature: city === "London" ? 15.5 : 25.0, - description: city === "London" ? "Cloudy" : "Sunny", - humidity: city === "London" ? 80 : 40, - }); - }, }; } diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 9ddf013af..bb285aaee 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -129,6 +129,13 @@ export async function setupOptions(options, context) { return; }, AsyncImportImports: getAsyncImportImports(importsContext), + fetchWeatherData: (city) => { + return Promise.resolve({ + temperature: city === "London" ? 15.5 : 25.0, + description: city === "London" ? "Cloudy" : "Sunny", + humidity: city === "London" ? 80 : 40, + }); + }, jsTranslatePoint: (point, dx, dy) => { return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) }; }, From 6d36763d230d0b57c8215512dda4a12dd2a77afb Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Mar 2026 19:49:31 +0000 Subject: [PATCH 15/20] Regenerate `fetchWeatherData` bridging code --- .../Generated/BridgeJS.Macros.swift | 11 + .../Generated/BridgeJS.swift | 213 ++++++++++++++++++ .../Generated/JavaScript/BridgeJS.json | 87 +++++++ 3 files changed, 311 insertions(+) diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index b2d0fb209..e3a6eec61 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -43,6 +43,17 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSFunction func runAsyncWorks() async throws(JSException) -> Void +@JSFunction func fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData + +@JSClass struct WeatherData { + @JSGetter var temperature: Double + @JSSetter func setTemperature(_ value: Double) throws(JSException) + @JSGetter var description: String + @JSSetter func setDescription(_ value: String) throws(JSException) + @JSGetter var humidity: Double + @JSSetter func setHumidity(_ value: Double) throws(JSException) +} + @JSFunction(jsName: "$jsWeirdFunction") func _jsWeirdFunction() throws(JSException) -> Double @JSClass(jsName: "$WeirdClass") struct _WeirdClass { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 06f4c8c00..71e5ef537 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -1736,6 +1736,67 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestss11 #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 @@ -12170,6 +12231,31 @@ func _$runAsyncWorks() async throws(JSException) -> Void { } } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_fetchWeatherData") +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void +#else +fileprivate func bjs_fetchWeatherData_extern(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_fetchWeatherData(_ resolveRef: Int32, _ rejectRef: Int32, _ cityBytes: Int32, _ cityLength: Int32) -> Void { + return bjs_fetchWeatherData_extern(resolveRef, rejectRef, cityBytes, cityLength) +} + +func _$fetchWeatherData(_ city: String) async throws(JSException) -> WeatherData { + let resolved = try await _bjs_awaitPromise(makeResolveClosure: { + JSTypedClosure<(sending WeatherData) -> Void>($0) + }, makeRejectClosure: { + JSTypedClosure<(sending JSValue) -> Void>($0) + }) { resolveRef, rejectRef in + city.bridgeJSWithLoweredParameter { (cityBytes, cityLength) in + bjs_fetchWeatherData(resolveRef, rejectRef, cityBytes, cityLength) + } + } + return resolved +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs__jsWeirdFunction") fileprivate func bjs__jsWeirdFunction_extern() -> Float64 @@ -12348,6 +12434,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 384b9a6c0..60b02020d 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -18616,6 +18616,29 @@ } } }, + { + "effects" : { + "isAsync" : true, + "isStatic" : false, + "isThrows" : true + }, + "name" : "fetchWeatherData", + "parameters" : [ + { + "name" : "city", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "WeatherData" + } + } + }, { "effects" : { "isAsync" : false, @@ -18766,6 +18789,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" : [ From b472133df36f0f20ba67b879bace74cd9bce43e6 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 26 Mar 2026 20:35:56 +0000 Subject: [PATCH 16/20] BridgeJS: Centralize closure sig collection in BridgeSkeletonWalker --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 78 +------------------ .../Sources/BridgeJSLink/BridgeJSLink.swift | 75 +----------------- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 58 ++++++++++++-- 3 files changed, 59 insertions(+), 152 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index d67c717f1..1e85bb148 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -188,82 +188,10 @@ public struct ClosureCodegen { } public func renderSupport(for skeleton: BridgeJSSkeleton) throws -> String? { - let collector = ClosureSignatureCollectorVisitor() - var walker = BridgeTypeWalker(visitor: collector) + let collector = ClosureSignatureCollectorVisitor(moduleName: skeleton.moduleName) + var walker = BridgeSkeletonWalker(visitor: collector) walker.walk(skeleton) - var closureSignatures = walker.visitor.signatures - - // When async imports exist, inject closure signatures for the typed resolve - // and reject callbacks used by _bjs_awaitPromise. - // - Reject always uses (sending JSValue) -> Void - // - Resolve uses a typed closure matching the return type (or () -> Void for void) - // All async callback closures use `sending` parameters so values can be - // transferred through the checked continuation without Sendable constraints. - if let imported = skeleton.imported { - 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, - sendingParameters: true - ) - ) - // 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, - sendingParameters: true - ) - ) - } - } - for type in file.types { - for method in (type.methods + type.staticMethods) where method.effects.isAsync { - closureSignatures.insert( - ClosureSignature( - parameters: [.jsValue], - returnType: .void, - moduleName: skeleton.moduleName, - sendingParameters: true - ) - ) - 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, - sendingParameters: true - ) - ) - } - } - } - } - } - + let closureSignatures = walker.visitor.signatures guard !closureSignatures.isEmpty else { return nil } var decls: [DeclSyntax] = [] diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index e1410126b..293a06537 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -636,79 +636,10 @@ public struct BridgeJSLink { for unified in skeletons { let moduleName = unified.moduleName - let collector = ClosureSignatureCollectorVisitor() - var walker = BridgeTypeWalker(visitor: collector) + let collector = ClosureSignatureCollectorVisitor(moduleName: moduleName) + var walker = BridgeSkeletonWalker(visitor: collector) walker.walk(unified) - var closureSignatures = walker.visitor.signatures - - // 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 { - // Reject callback - closureSignatures.insert( - ClosureSignature( - parameters: [.jsValue], - returnType: .void, - moduleName: moduleName, - sendingParameters: true - ) - ) - // 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, - sendingParameters: true - ) - ) - } - } - for type in file.types { - for method in (type.methods + type.staticMethods) where method.effects.isAsync { - closureSignatures.insert( - ClosureSignature( - parameters: [.jsValue], - returnType: .void, - moduleName: moduleName, - sendingParameters: true - ) - ) - if method.returnType == .void { - closureSignatures.insert( - ClosureSignature( - parameters: [], - returnType: .void, - moduleName: moduleName - ) - ) - } else { - closureSignatures.insert( - ClosureSignature( - parameters: [method.returnType], - returnType: .void, - moduleName: moduleName, - sendingParameters: true - ) - ) - } - } - } - } - } - + let closureSignatures = walker.visitor.signatures guard !closureSignatures.isEmpty else { continue } intrinsicRegistry.register(name: "swiftClosureHelpers") { helperPrinter in diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index f61f7e2cf..c5672c79c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -395,13 +395,19 @@ public struct Parameter: Codable, Equatable, Sendable { } } -// MARK: - BridgeType Visitor +// MARK: - BridgeSkeleton Visitor -public protocol BridgeTypeVisitor { +public protocol BridgeSkeletonVisitor { mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) + mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) } -public struct BridgeTypeWalker { +public extension BridgeSkeletonVisitor { + mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) {} + mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) {} +} + +public struct BridgeSkeletonWalker { public var visitor: Visitor public init(visitor: Visitor) { @@ -487,6 +493,7 @@ public struct BridgeTypeWalker { } } public mutating func walk(_ function: ImportedFunctionSkeleton) { + visitor.visitImportedFunction(function) walk(function.parameters) walk(function.returnType) } @@ -1160,16 +1167,57 @@ public struct ImportedModuleSkeleton: Codable { // MARK: - Closure signature collection visitor -public struct ClosureSignatureCollectorVisitor: BridgeTypeVisitor { +public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { public var signatures: Set = [] + let moduleName: String - public init(signatures: Set = []) { + public init(moduleName: String, signatures: Set = []) { + self.moduleName = moduleName self.signatures = signatures } public mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) { signatures.insert(signature) } + public mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) { + guard function.effects.isAsync else { return } + + // When async imports exist, inject closure signatures for the typed resolve + // and reject callbacks used by _bjs_awaitPromise. + // - Reject always uses (sending JSValue) -> Void + // - Resolve uses a typed closure matching the return type (or () -> Void for void) + // All async callback closures use `sending` parameters so values can be + // transferred through the checked continuation without Sendable constraints. + + // Reject callback + signatures.insert( + ClosureSignature( + parameters: [.jsValue], + returnType: .void, + moduleName: moduleName, + sendingParameters: true + ) + ) + // Resolve callback (typed per return type) + if function.returnType == .void { + signatures.insert( + ClosureSignature( + parameters: [], + returnType: .void, + moduleName: moduleName + ) + ) + } else { + signatures.insert( + ClosureSignature( + parameters: [function.returnType], + returnType: .void, + moduleName: moduleName, + sendingParameters: true + ) + ) + } + } } // MARK: - Unified Skeleton From 14561d4544475bb672e28a331c734e6b2e7e5151 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 26 Mar 2026 21:00:13 +0000 Subject: [PATCH 17/20] BridgeJS: Stop spreading isAsync handling outside of CallJSEmission --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 1 + .../Sources/BridgeJSCore/ExportSwift.swift | 3 + .../Sources/BridgeJSCore/ImportTS.swift | 114 +++++++++--------- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index 1e85bb148..f3ed97ba3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -32,6 +32,7 @@ public struct ClosureCodegen { let builder = try ImportTS.CallJSEmission( moduleName: "bjs", abiName: externName, + effects: Effects(isAsync: signature.isAsync, isThrows: signature.isThrows), returnType: signature.returnType, context: .exportSwift ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 5b6c7d1ff..ca7bdc3c2 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -1228,6 +1228,7 @@ struct ProtocolCodegen { let builder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: "_extern_\(method.name)", + effects: method.effects, returnType: method.returnType, context: .exportSwift ) @@ -1327,6 +1328,7 @@ struct ProtocolCodegen { let getterBuilder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: getterAbiName, + effects: Effects(isAsync: false, isThrows: false), returnType: property.type, context: .exportSwift ) @@ -1360,6 +1362,7 @@ struct ProtocolCodegen { let setterBuilder = try ImportTS.CallJSEmission( moduleName: moduleName, abiName: setterAbiName, + effects: Effects(isAsync: false, isThrows: false), returnType: .void, context: .exportSwift ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 1d5f40c12..a991e802b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -69,6 +69,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: nil), + effects: Effects(isAsync: false, isThrows: true), returnType: getter.type ) try builder.call() @@ -94,13 +95,14 @@ public struct ImportTS { let abiName: String let moduleName: String + let effects: Effects let returnType: BridgeType let context: BridgeContext var body = CodeFragmentPrinter() var abiParameterForwardings: [String] = [] var abiParameterSignatures: [(name: String, type: WasmCoreType)] = [] - var abiReturnType: WasmCoreType? + let abiReturnType: WasmCoreType? // Track destructured variable names for multiple lowered parameters var destructuredVarNames: [String] = [] // Stack-lowered parameters should be evaluated in reverse order to match LIFO stacks @@ -111,15 +113,29 @@ public struct ImportTS { private var borrowedArguments: [BorrowedArgument] = [] private let needsReturnVariable: Bool - init(moduleName: String, abiName: String, returnType: BridgeType, context: BridgeContext = .importTS) throws { + init( + moduleName: String, + abiName: String, + effects: Effects, + returnType: BridgeType, + context: BridgeContext = .importTS + ) throws { self.moduleName = moduleName self.abiName = abiName + self.effects = effects self.returnType = returnType self.context = context let liftingInfo = try returnType.liftingReturnInfo(context: context) - needsReturnVariable = - !(returnType == .void || returnType.usesSideChannelForOptionalReturn() - || liftingInfo.valueToLift == nil) + if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn() { + abiReturnType = nil + } else { + abiReturnType = liftingInfo.valueToLift + } + needsReturnVariable = abiReturnType != nil + + if effects.isAsync { + prependClosureCallbackParams() + } } func lowerParameter(param: Parameter) throws { @@ -216,12 +232,12 @@ public struct ImportTS { /// /// Used for async imports where the JS side receives closure-backed /// resolve/reject callbacks as object references. - func prependClosureCallbackParams() { + private func prependClosureCallbackParams() { abiParameterSignatures.insert(contentsOf: [("resolveRef", .i32), ("rejectRef", .i32)], at: 0) abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0) } - func call(skipExceptionCheck: Bool = false) throws { + func call() throws { for stmt in stackLoweringStmts { body.write(stmt.description) } @@ -254,25 +270,30 @@ public struct ImportTS { // Add exception check for ImportTS context (skipped for async, where // errors are funneled through the JS-side reject path) - if !skipExceptionCheck && context == .importTS { + if !effects.isAsync && context == .importTS { body.write("if let error = _swift_js_take_exception() { throw error }") } } func liftReturnValue() throws { + if effects.isAsync { + liftAsyncReturnValue() + } else { + try liftSyncReturnValue() + } + } + + private func liftSyncReturnValue() throws { let liftingInfo = try returnType.liftingReturnInfo(context: context) if returnType == .void { - abiReturnType = nil return } if returnType.usesSideChannelForOptionalReturn() { // Side channel returns: extern function returns Void, value is retrieved via side channel - abiReturnType = nil body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()") } else { - abiReturnType = liftingInfo.valueToLift let liftExpr: String switch returnType { case .closure(let signature, _): @@ -288,25 +309,24 @@ public struct ImportTS { } } - func liftAsyncReturnValue(originalReturnType: BridgeType) { + private func liftAsyncReturnValue() { // For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32` // and returns void. The JS side calls the resolve/reject closures when the Promise settles. // The resolve closure is typed to match the return type, so the ABI conversion is handled // by the existing closure codegen infrastructure — no manual JSValue-to-type switch needed. - abiReturnType = nil // Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise let innerBody = body body = CodeFragmentPrinter() let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }" - if originalReturnType == .void { + if returnType == .void { let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }" body.write( "try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in" ) } else { - let resolveSwiftType = originalReturnType.closureSwiftType + let resolveSwiftType = returnType.closureSwiftType let resolveFactory = "makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }" body.write( @@ -318,19 +338,11 @@ public struct ImportTS { } body.write("}") - if originalReturnType != .void { + if returnType != .void { body.write("return resolved") } } - func assignThis(returnType: BridgeType) { - guard case .jsObject = returnType else { - preconditionFailure("assignThis can only be called with a jsObject return type") - } - abiReturnType = .i32 - body.write("self.jsObject = JSObject(id: UInt32(bitPattern: ret))") - } - func renderImportDecl() -> DeclSyntax { let printer = CodeFragmentPrinter() SwiftCodePattern.buildExternFunctionDecl( @@ -408,26 +420,17 @@ public struct ImportTS { _ function: ImportedFunctionSkeleton, topLevelDecls: inout [DeclSyntax] ) throws -> [DeclSyntax] { - // 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 + effects: function.effects, + returnType: function.returnType ) - if function.effects.isAsync { - builder.prependClosureCallbackParams() - } for param in function.parameters { try builder.lowerParameter(param: param) } - try builder.call(skipExceptionCheck: function.effects.isAsync) - if function.effects.isAsync { - builder.liftAsyncReturnValue(originalReturnType: function.returnType) - } else { - try builder.liftReturnValue() - } + try builder.call() + try builder.liftReturnValue() topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( @@ -445,25 +448,18 @@ public struct ImportTS { var decls: [DeclSyntax] = [] func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { - let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType let builder = try CallJSEmission( moduleName: moduleName, abiName: method.abiName(context: type), - returnType: abiReturnType + effects: method.effects, + returnType: method.returnType ) - if method.effects.isAsync { - builder.prependClosureCallbackParams() - } try builder.lowerParameter(param: selfParameter) for param in method.parameters { try builder.lowerParameter(param: param) } - try builder.call(skipExceptionCheck: method.effects.isAsync) - if method.effects.isAsync { - builder.liftAsyncReturnValue(originalReturnType: method.returnType) - } else { - try builder.liftReturnValue() - } + try builder.call() + try builder.liftReturnValue() topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( @@ -477,20 +473,17 @@ public struct ImportTS { func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] { let abiName = method.abiName(context: type, operation: "static") - let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType - let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, returnType: abiReturnType) - if method.effects.isAsync { - builder.prependClosureCallbackParams() - } + let builder = try CallJSEmission( + moduleName: moduleName, + abiName: abiName, + effects: method.effects, + returnType: method.returnType + ) for param in method.parameters { try builder.lowerParameter(param: param) } - try builder.call(skipExceptionCheck: method.effects.isAsync) - if method.effects.isAsync { - builder.liftAsyncReturnValue(originalReturnType: method.returnType) - } else { - try builder.liftReturnValue() - } + try builder.call() + try builder.liftReturnValue() topLevelDecls.append(builder.renderImportDecl()) return [ builder.renderThunkDecl( @@ -506,6 +499,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: constructor.abiName(context: type), + effects: Effects(isAsync: false, isThrows: true), returnType: .jsObject(nil) ) for param in constructor.parameters { @@ -527,6 +521,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: type), + effects: Effects(isAsync: false, isThrows: true), returnType: getter.type ) try builder.lowerParameter(param: selfParameter) @@ -546,6 +541,7 @@ public struct ImportTS { let builder = try CallJSEmission( moduleName: moduleName, abiName: setter.abiName(context: type), + effects: Effects(isAsync: false, isThrows: true), returnType: .void ) let newValue = Parameter(label: nil, name: "newValue", type: setter.type) From 4bfdfe72596d4697376e2c023867d2e67069466a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 26 Mar 2026 21:05:13 +0000 Subject: [PATCH 18/20] BridgeJS: Remove error-prone default effects in thunk generation --- .../Sources/BridgeJSCore/ImportTS.swift | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index a991e802b..536c7eeab 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -66,10 +66,11 @@ public struct ImportTS { _ getter: ImportedGetterSkeleton, topLevelDecls: inout [DeclSyntax] ) throws -> [DeclSyntax] { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: nil), - effects: Effects(isAsync: false, isThrows: true), + effects: effects, returnType: getter.type ) try builder.call() @@ -79,7 +80,8 @@ public struct ImportTS { builder.renderThunkDecl( name: "_$\(getter.name)_get", parameters: [], - returnType: getter.type + returnType: getter.type, + effects: effects ) .with(\.leadingTrivia, Self.renderDocumentation(documentation: getter.documentation)) ] @@ -360,7 +362,7 @@ public struct ImportTS { name: String, parameters: [Parameter], returnType: BridgeType, - effects: Effects = Effects(isAsync: false, isThrows: true) + effects: Effects ) -> DeclSyntax { let printer = CodeFragmentPrinter() let signature = SwiftSignatureBuilder.buildFunctionSignature( @@ -496,10 +498,11 @@ public struct ImportTS { } func renderConstructorDecl(constructor: ImportedConstructorSkeleton) throws -> [DeclSyntax] { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: constructor.abiName(context: type), - effects: Effects(isAsync: false, isThrows: true), + effects: effects, returnType: .jsObject(nil) ) for param in constructor.parameters { @@ -512,16 +515,18 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type), parameters: constructor.parameters, - returnType: .jsObject(nil) + returnType: .jsObject(nil), + effects: effects ) ] } func renderGetterDecl(getter: ImportedGetterSkeleton) throws -> DeclSyntax { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: getter.abiName(context: type), - effects: Effects(isAsync: false, isThrows: true), + effects: effects, returnType: getter.type ) try builder.lowerParameter(param: selfParameter) @@ -532,16 +537,18 @@ public struct ImportTS { builder.renderThunkDecl( name: Self.thunkName(type: type, propertyName: getter.name, operation: "get"), parameters: [selfParameter], - returnType: getter.type + returnType: getter.type, + effects: effects ) ) } func renderSetterDecl(setter: ImportedSetterSkeleton) throws -> DeclSyntax { + let effects = Effects(isAsync: false, isThrows: true) let builder = try CallJSEmission( moduleName: moduleName, abiName: setter.abiName(context: type), - effects: Effects(isAsync: false, isThrows: true), + effects: effects, returnType: .void ) let newValue = Parameter(label: nil, name: "newValue", type: setter.type) @@ -561,7 +568,8 @@ public struct ImportTS { return builder.renderThunkDecl( name: Self.thunkName(type: type, propertyName: propertyNameForThunk, operation: "set"), parameters: [selfParameter, newValue], - returnType: .void + returnType: .void, + effects: effects ) } if let constructor = type.constructor { From a03b585d10ec9b247f9f92db54ea0e94f8c654f0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 26 Mar 2026 21:25:53 +0000 Subject: [PATCH 19/20] BridgeJSLink: Centralize async handling in ImportedThunkBuilder --- .../Sources/BridgeJSLink/BridgeJSLink.swift | 274 ++++++++---------- 1 file changed, 128 insertions(+), 146 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 293a06537..41fbfa5bf 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -741,7 +741,12 @@ public struct BridgeJSLink { signature: ClosureSignature, functionName: String ) throws -> [String] { - let thunkBuilder = ImportedThunkBuilder(context: .exportSwift, intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: signature.isAsync, isThrows: signature.isThrows), + returnType: signature.returnType, + context: .exportSwift, + intrinsicRegistry: intrinsicRegistry + ) thunkBuilder.parameterNames.append("callbackId") thunkBuilder.body.write("const callback = \(JSGlueVariableScope.reservedSwift).memory.getObject(callbackId);") @@ -750,13 +755,9 @@ public struct BridgeJSLink { try thunkBuilder.liftParameter(param: Parameter(label: nil, name: paramName, type: paramType)) } - let returnExpr = try thunkBuilder.call(calleeExpr: "callback", returnType: signature.returnType) + try thunkBuilder.call(calleeExpr: "callback", returnType: signature.returnType) - var functionLines = thunkBuilder.renderFunction( - name: nil, - returnExpr: returnExpr, - returnType: signature.returnType - ) + var functionLines = thunkBuilder.renderFunction(name: nil) functionLines[0] = "bjs[\"\(functionName)\"] = " + functionLines[0] return functionLines @@ -2163,15 +2164,25 @@ extension BridgeJSLink { class ImportedThunkBuilder { let body: CodeFragmentPrinter let scope: JSGlueVariableScope + let effects: Effects + let returnType: BridgeType let context: BridgeContext var parameterNames: [String] = [] var parameterForwardings: [String] = [] + var returnExpr: String? let printContext: IntrinsicJSFragment.PrintCodeContext - init(context: BridgeContext = .importTS, intrinsicRegistry: JSIntrinsicRegistry) { + init( + effects: Effects, + returnType: BridgeType, + context: BridgeContext = .importTS, + intrinsicRegistry: JSIntrinsicRegistry + ) { self.body = CodeFragmentPrinter() self.scope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) self.context = context + self.effects = effects + self.returnType = returnType self.printContext = IntrinsicJSFragment.PrintCodeContext( scope: scope, printer: body, @@ -2201,7 +2212,15 @@ extension BridgeJSLink { parameterForwardings.append(contentsOf: liftedValues) } - func renderFunction( + func renderFunction(name: String?) -> [String] { + if effects.isAsync { + return renderAsyncFunction(name: name) + } else { + return renderSyncFunction(name: name, returnExpr: returnExpr, returnType: returnType) + } + } + + private func renderSyncFunction( name: String?, returnExpr: String?, returnType: BridgeType @@ -2231,21 +2250,12 @@ extension BridgeJSLink { return printer.lines } - /// Generates the call expression for an async import. - /// - /// 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("\(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, /// 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] { + private func renderAsyncFunction(name: String?) -> [String] { let printer = CodeFragmentPrinter() let allParams = ["resolveRef", "rejectRef"] + parameterNames printer.write("function\(name.map { " \($0)" } ?? "")(\(allParams.joined(separator: ", "))) {") @@ -2259,21 +2269,25 @@ extension BridgeJSLink { return printer.lines } - func call(name: String, fromObjectExpr: String, returnType: BridgeType) throws -> String? { + func call(name: String, fromObjectExpr: String, returnType: BridgeType) throws { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) return try self.call(calleeExpr: calleeExpr, returnType: returnType) } - func call(name: String, returnType: BridgeType) throws -> String? { + func call(name: String, returnType: BridgeType) throws { return try call(name: name, fromObjectExpr: "imports", returnType: returnType) } - func call(calleeExpr: String, returnType: BridgeType) throws -> String? { + func call(calleeExpr: String, returnType: BridgeType) throws { let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - return try self.call(callExpr: callExpr, returnType: returnType) + try self.call(callExpr: callExpr, returnType: returnType) } - private func call(callExpr: String, returnType: BridgeType) throws -> String? { + private func call(callExpr: String, returnType: BridgeType) throws { + if effects.isAsync { + body.write("\(callExpr).then(resolve, reject);") + return + } let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: returnType, context: context) let returnExpr: String? if loweringFragment.parameters.count == 0 { @@ -2284,26 +2298,30 @@ extension BridgeJSLink { body.write("let \(resultVariable) = \(callExpr);") returnExpr = resultVariable } - return try lowerReturnValue( + self.returnExpr = try lowerReturnValue( returnType: returnType, returnExpr: returnExpr, loweringFragment: loweringFragment ) } - func callConstructor(jsName: String, swiftTypeName: String, fromObjectExpr: String) throws -> String? { + func callConstructor(jsName: String, swiftTypeName: String, fromObjectExpr: String) throws { let ctorExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: jsName) let call = "new \(ctorExpr)(\(parameterForwardings.joined(separator: ", ")))" let type: BridgeType = .jsObject(swiftTypeName) let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: type, context: context) - return try lowerReturnValue(returnType: type, returnExpr: call, loweringFragment: loweringFragment) + self.returnExpr = try lowerReturnValue( + returnType: type, + returnExpr: call, + loweringFragment: loweringFragment + ) } - func callConstructor(jsName: String, swiftTypeName: String) throws -> String? { + func callConstructor(jsName: String, swiftTypeName: String) throws { return try callConstructor(jsName: jsName, swiftTypeName: swiftTypeName, fromObjectExpr: "imports") } - func callMethod(name: String, returnType: BridgeType) throws -> String? { + func callMethod(name: String, returnType: BridgeType) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) return try call( @@ -2320,7 +2338,7 @@ extension BridgeJSLink { body.write("\(callExpr).then(resolve, reject);") } - func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws -> String? { + func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws { let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) return try call( calleeExpr: calleeExpr, @@ -2328,14 +2346,7 @@ extension BridgeJSLink { ) } - /// Generates an async static method call with resolve/reject closure refs. - func callAsyncStaticMethod(on objectExpr: String, name: String) { - let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - body.write("\(callExpr).then(resolve, reject);") - } - - func callPropertyGetter(name: String, returnType: BridgeType) throws -> String? { + func callPropertyGetter(name: String, returnType: BridgeType) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) if context == .exportSwift, returnType.usesSideChannelForOptionalReturn() { @@ -2350,8 +2361,9 @@ extension BridgeJSLink { let fragment = try IntrinsicJSFragment.protocolPropertyOptionalToSideChannel(wrappedType: wrappedType) _ = try fragment.printCode([resultVar], printContext) - - return nil // Side-channel types return nil (no direct return value) + // Side-channel types return nil (no direct return value) + self.returnExpr = nil + return } return try call( @@ -2367,7 +2379,7 @@ extension BridgeJSLink { body.write("\(call);") } - func getImportProperty(name: String, fromObjectExpr: String, returnType: BridgeType) throws -> String? { + func getImportProperty(name: String, fromObjectExpr: String, returnType: BridgeType) throws { if returnType == .void { throw BridgeJSLinkError(message: "Void is not supported for imported JS properties") } @@ -2385,14 +2397,14 @@ extension BridgeJSLink { returnExpr = resultVariable } - return try lowerReturnValue( + self.returnExpr = try lowerReturnValue( returnType: returnType, returnExpr: returnExpr, loweringFragment: loweringFragment ) } - func getImportProperty(name: String, returnType: BridgeType) throws -> String? { + func getImportProperty(name: String, returnType: BridgeType) throws { return try getImportProperty(name: name, fromObjectExpr: "imports", returnType: returnType) } @@ -3160,33 +3172,23 @@ extension BridgeJSLink { importObjectBuilder: ImportObjectBuilder, function: ImportedFunctionSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: function.effects, + returnType: function.returnType, + intrinsicRegistry: intrinsicRegistry + ) for param in function.parameters { try thunkBuilder.liftParameter(param: param) } let jsName = function.jsName ?? function.name let importRootExpr = function.from == .global ? "globalThis" : "imports" - let 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 - ) - } + try thunkBuilder.call( + name: jsName, + fromObjectExpr: importRootExpr, + returnType: function.returnType + ) + let funcLines = thunkBuilder.renderFunction(name: function.abiName(context: nil)) if function.from == nil { importObjectBuilder.appendDts( [ @@ -3201,20 +3203,20 @@ extension BridgeJSLink { importObjectBuilder: ImportObjectBuilder, getter: ImportedGetterSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: getter.type, + intrinsicRegistry: intrinsicRegistry + ) let jsName = getter.jsName ?? getter.name let importRootExpr = getter.from == .global ? "globalThis" : "imports" - let returnExpr = try thunkBuilder.getImportProperty( + try thunkBuilder.getImportProperty( name: jsName, fromObjectExpr: importRootExpr, returnType: getter.type ) let abiName = getter.abiName(context: nil) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: getter.type - ) + let funcLines = thunkBuilder.renderFunction(name: abiName) if getter.from == nil { importObjectBuilder.appendDts(["readonly \(renderTSPropertyName(jsName)): \(getter.type.tsType);"]) } @@ -3258,7 +3260,6 @@ extension BridgeJSLink { param: Parameter(label: nil, name: "newValue", type: setter.type) ) thunkBuilder.callPropertySetter(name: setter.jsName ?? setter.name, returnType: setter.type) - return nil } ) importObjectBuilder.assignToImportObject(name: setterAbiName, function: js) @@ -3302,55 +3303,54 @@ extension BridgeJSLink { type: ImportedTypeSkeleton, constructor: ImportedConstructorSkeleton ) throws { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: BridgeType.jsObject(type.name), + intrinsicRegistry: intrinsicRegistry + ) for param in constructor.parameters { try thunkBuilder.liftParameter(param: param) } - let returnType = BridgeType.jsObject(type.name) let importRootExpr = type.from == .global ? "globalThis" : "imports" - let returnExpr = try thunkBuilder.callConstructor( + try thunkBuilder.callConstructor( jsName: type.jsName ?? type.name, swiftTypeName: type.name, fromObjectExpr: importRootExpr ) let abiName = constructor.abiName(context: type) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: returnType - ) + let funcLines = thunkBuilder.renderFunction(name: abiName) importObjectBuilder.assignToImportObject(name: abiName, function: funcLines) } func renderImportedGetter( getter: ImportedGetterSkeleton, abiName: String, - emitCall: (ImportedThunkBuilder) throws -> String? + emitCall: (ImportedThunkBuilder) throws -> Void ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) - thunkBuilder.liftSelf() - let returnExpr = try emitCall(thunkBuilder) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: getter.type + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: getter.type, + intrinsicRegistry: intrinsicRegistry ) + thunkBuilder.liftSelf() + try emitCall(thunkBuilder) + let funcLines = thunkBuilder.renderFunction(name: abiName) return (funcLines, []) } func renderImportedSetter( setter: ImportedSetterSkeleton, abiName: String, - emitCall: (ImportedThunkBuilder) throws -> String? + emitCall: (ImportedThunkBuilder) throws -> Void ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) - thunkBuilder.liftSelf() - let returnExpr = try emitCall(thunkBuilder) - let funcLines = thunkBuilder.renderFunction( - name: abiName, - returnExpr: returnExpr, - returnType: .void + let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: .void, + intrinsicRegistry: intrinsicRegistry ) + thunkBuilder.liftSelf() + try emitCall(thunkBuilder) + let funcLines = thunkBuilder.renderFunction(name: abiName) return (funcLines, []) } @@ -3358,7 +3358,11 @@ extension BridgeJSLink { context: ImportedTypeSkeleton, method: ImportedFunctionSkeleton ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: method.effects, + returnType: method.returnType, + intrinsicRegistry: intrinsicRegistry + ) for param in method.parameters { try thunkBuilder.liftParameter(param: param) } @@ -3368,22 +3372,12 @@ extension BridgeJSLink { propertyName: context.jsName ?? context.name ) - let funcLines: [String] - if method.effects.isAsync { - thunkBuilder.callAsyncStaticMethod(on: constructorExpr, name: method.jsName ?? method.name) - funcLines = thunkBuilder.renderAsyncFunction(name: method.abiName(context: context, operation: "static")) - } else { - let returnExpr = try thunkBuilder.callStaticMethod( - on: constructorExpr, - name: method.jsName ?? method.name, - returnType: method.returnType - ) - funcLines = thunkBuilder.renderFunction( - name: method.abiName(context: context, operation: "static"), - returnExpr: returnExpr, - returnType: method.returnType - ) - } + try thunkBuilder.callStaticMethod( + on: constructorExpr, + name: method.jsName ?? method.name, + returnType: method.returnType + ) + let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context, operation: "static")) return (funcLines, []) } @@ -3391,27 +3385,21 @@ extension BridgeJSLink { context: ImportedTypeSkeleton, method: ImportedFunctionSkeleton ) throws -> (js: [String], dts: [String]) { - let thunkBuilder = ImportedThunkBuilder(intrinsicRegistry: intrinsicRegistry) + let thunkBuilder = ImportedThunkBuilder( + effects: method.effects, + returnType: method.returnType, + intrinsicRegistry: intrinsicRegistry + ) thunkBuilder.liftSelf() for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let 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 - ) - } + try thunkBuilder.callMethod( + name: method.jsName ?? method.name, + returnType: method.returnType + ) + let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context)) return (funcLines, []) } @@ -3429,16 +3417,14 @@ extension BridgeJSLink { ) let getterThunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: true), + returnType: property.type, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) getterThunkBuilder.liftSelf() - let returnExpr = try getterThunkBuilder.callPropertyGetter(name: property.name, returnType: property.type) - let getterLines = getterThunkBuilder.renderFunction( - name: getterAbiName, - returnExpr: returnExpr, - returnType: property.type - ) + try getterThunkBuilder.callPropertyGetter(name: property.name, returnType: property.type) + let getterLines = getterThunkBuilder.renderFunction(name: getterAbiName) importObjectBuilder.assignToImportObject(name: getterAbiName, function: getterLines) if !property.isReadonly { @@ -3450,6 +3436,8 @@ extension BridgeJSLink { className: `protocol`.name ) let setterThunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false), + returnType: .void, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) @@ -3458,11 +3446,7 @@ extension BridgeJSLink { param: Parameter(label: nil, name: "value", type: property.type) ) setterThunkBuilder.callPropertySetter(name: property.name, returnType: property.type) - let setterLines = setterThunkBuilder.renderFunction( - name: setterAbiName, - returnExpr: nil, - returnType: .void - ) + let setterLines = setterThunkBuilder.renderFunction(name: setterAbiName) importObjectBuilder.assignToImportObject(name: setterAbiName, function: setterLines) } } @@ -3473,6 +3457,8 @@ extension BridgeJSLink { method: ExportedFunction ) throws { let thunkBuilder = ImportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false), + returnType: method.returnType, context: .exportSwift, intrinsicRegistry: intrinsicRegistry ) @@ -3480,12 +3466,8 @@ extension BridgeJSLink { for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - let returnExpr = try thunkBuilder.callMethod(name: method.name, returnType: method.returnType) - let funcLines = thunkBuilder.renderFunction( - name: method.abiName, - returnExpr: returnExpr, - returnType: method.returnType - ) + try thunkBuilder.callMethod(name: method.name, returnType: method.returnType) + let funcLines = thunkBuilder.renderFunction(name: method.abiName) importObjectBuilder.assignToImportObject(name: method.abiName, function: funcLines) } } From 22e74342034b8512ab9f8348ca83cb5cc839f95f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 26 Mar 2026 21:30:27 +0000 Subject: [PATCH 20/20] BridgeJS: Remove reundant returnType from `call` family of methods in ImportedThunkBuilder --- .../Sources/BridgeJSLink/BridgeJSLink.swift | 69 +++++++------------ 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 41fbfa5bf..63b628eb3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -755,7 +755,7 @@ public struct BridgeJSLink { try thunkBuilder.liftParameter(param: Parameter(label: nil, name: paramName, type: paramType)) } - try thunkBuilder.call(calleeExpr: "callback", returnType: signature.returnType) + try thunkBuilder.call(calleeExpr: "callback") var functionLines = thunkBuilder.renderFunction(name: nil) functionLines[0] = "bjs[\"\(functionName)\"] = " + functionLines[0] @@ -2269,21 +2269,21 @@ extension BridgeJSLink { return printer.lines } - func call(name: String, fromObjectExpr: String, returnType: BridgeType) throws { + func call(name: String, fromObjectExpr: String) throws { let calleeExpr = Self.propertyAccessExpr(objectExpr: fromObjectExpr, propertyName: name) - return try self.call(calleeExpr: calleeExpr, returnType: returnType) + return try self.call(calleeExpr: calleeExpr) } - func call(name: String, returnType: BridgeType) throws { - return try call(name: name, fromObjectExpr: "imports", returnType: returnType) + func call(name: String) throws { + return try call(name: name, fromObjectExpr: "imports") } - func call(calleeExpr: String, returnType: BridgeType) throws { + func call(calleeExpr: String) throws { let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))" - try self.call(callExpr: callExpr, returnType: returnType) + try self.call(callExpr: callExpr) } - private func call(callExpr: String, returnType: BridgeType) throws { + private func call(callExpr: String) throws { if effects.isAsync { body.write("\(callExpr).then(resolve, reject);") return @@ -2321,13 +2321,10 @@ extension BridgeJSLink { return try callConstructor(jsName: jsName, swiftTypeName: swiftTypeName, fromObjectExpr: "imports") } - func callMethod(name: String, returnType: BridgeType) throws { + func callMethod(name: String) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - return try call( - calleeExpr: calleeExpr, - returnType: returnType - ) + return try call(calleeExpr: calleeExpr) } /// Generates an async method call with resolve/reject closure refs. @@ -2338,15 +2335,12 @@ extension BridgeJSLink { body.write("\(callExpr).then(resolve, reject);") } - func callStaticMethod(on objectExpr: String, name: String, returnType: BridgeType) throws { + func callStaticMethod(on objectExpr: String, name: String) throws { let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - return try call( - calleeExpr: calleeExpr, - returnType: returnType - ) + return try call(calleeExpr: calleeExpr) } - func callPropertyGetter(name: String, returnType: BridgeType) throws { + func callPropertyGetter(name: String) throws { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) if context == .exportSwift, returnType.usesSideChannelForOptionalReturn() { @@ -2366,13 +2360,10 @@ extension BridgeJSLink { return } - return try call( - callExpr: accessExpr, - returnType: returnType - ) + return try call(callExpr: accessExpr) } - func callPropertySetter(name: String, returnType: BridgeType) { + func callPropertySetter(name: String) { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) let call = "\(accessExpr) = \(parameterForwardings.joined(separator: ", "))" @@ -3183,11 +3174,7 @@ extension BridgeJSLink { let jsName = function.jsName ?? function.name let importRootExpr = function.from == .global ? "globalThis" : "imports" - try thunkBuilder.call( - name: jsName, - fromObjectExpr: importRootExpr, - returnType: function.returnType - ) + try thunkBuilder.call(name: jsName, fromObjectExpr: importRootExpr) let funcLines = thunkBuilder.renderFunction(name: function.abiName(context: nil)) if function.from == nil { importObjectBuilder.appendDts( @@ -3240,10 +3227,7 @@ extension BridgeJSLink { getter: getter, abiName: getterAbiName, emitCall: { thunkBuilder in - return try thunkBuilder.callPropertyGetter( - name: getter.jsName ?? getter.name, - returnType: getter.type - ) + return try thunkBuilder.callPropertyGetter(name: getter.jsName ?? getter.name) } ) importObjectBuilder.assignToImportObject(name: getterAbiName, function: js) @@ -3259,7 +3243,7 @@ extension BridgeJSLink { try thunkBuilder.liftParameter( param: Parameter(label: nil, name: "newValue", type: setter.type) ) - thunkBuilder.callPropertySetter(name: setter.jsName ?? setter.name, returnType: setter.type) + thunkBuilder.callPropertySetter(name: setter.jsName ?? setter.name) } ) importObjectBuilder.assignToImportObject(name: setterAbiName, function: js) @@ -3372,11 +3356,7 @@ extension BridgeJSLink { propertyName: context.jsName ?? context.name ) - try thunkBuilder.callStaticMethod( - on: constructorExpr, - name: method.jsName ?? method.name, - returnType: method.returnType - ) + try thunkBuilder.callStaticMethod(on: constructorExpr, name: method.jsName ?? method.name) let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context, operation: "static")) return (funcLines, []) } @@ -3395,10 +3375,7 @@ extension BridgeJSLink { try thunkBuilder.liftParameter(param: param) } - try thunkBuilder.callMethod( - name: method.jsName ?? method.name, - returnType: method.returnType - ) + try thunkBuilder.callMethod(name: method.jsName ?? method.name) let funcLines = thunkBuilder.renderFunction(name: method.abiName(context: context)) return (funcLines, []) } @@ -3423,7 +3400,7 @@ extension BridgeJSLink { intrinsicRegistry: intrinsicRegistry ) getterThunkBuilder.liftSelf() - try getterThunkBuilder.callPropertyGetter(name: property.name, returnType: property.type) + try getterThunkBuilder.callPropertyGetter(name: property.name) let getterLines = getterThunkBuilder.renderFunction(name: getterAbiName) importObjectBuilder.assignToImportObject(name: getterAbiName, function: getterLines) @@ -3445,7 +3422,7 @@ extension BridgeJSLink { try setterThunkBuilder.liftParameter( param: Parameter(label: nil, name: "value", type: property.type) ) - setterThunkBuilder.callPropertySetter(name: property.name, returnType: property.type) + setterThunkBuilder.callPropertySetter(name: property.name) let setterLines = setterThunkBuilder.renderFunction(name: setterAbiName) importObjectBuilder.assignToImportObject(name: setterAbiName, function: setterLines) } @@ -3466,7 +3443,7 @@ extension BridgeJSLink { for param in method.parameters { try thunkBuilder.liftParameter(param: param) } - try thunkBuilder.callMethod(name: method.name, returnType: method.returnType) + try thunkBuilder.callMethod(name: method.name) let funcLines = thunkBuilder.renderFunction(name: method.abiName) importObjectBuilder.assignToImportObject(name: method.abiName, function: funcLines) }