diff --git a/Package.swift b/Package.swift index 3657bfa9..bb0540ba 100644 --- a/Package.swift +++ b/Package.swift @@ -140,7 +140,7 @@ let package = Package( ), .testTarget( name: "BridgeJSRuntimeTests", - dependencies: ["JavaScriptKit"], + dependencies: ["JavaScriptKit", "JavaScriptEventLoop"], exclude: ["Generated/JavaScript"], swiftSettings: [ .enableExperimentalFeature("Extern") diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index f1605670..bc7fd56c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -113,10 +113,13 @@ struct BridgeJSLink { let tmpRetBytes; let tmpRetException; return { - /** @param {WebAssembly.Imports} importObject */ - addImports: (importObject) => { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { const bjs = {}; importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); bjs["return_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); tmpRetString = textDecoder.decode(bytes); @@ -223,6 +226,14 @@ struct BridgeJSLink { } func call(abiName: String, returnType: BridgeType) -> String? { + if effects.isAsync { + return _call(abiName: abiName, returnType: .jsObject(nil)) + } else { + return _call(abiName: abiName, returnType: returnType) + } + } + + private func _call(abiName: String, returnType: BridgeType) -> String? { let call = "instance.exports.\(abiName)(\(parameterForwardings.joined(separator: ", ")))" var returnExpr: String? @@ -296,8 +307,14 @@ struct BridgeJSLink { } } - private func renderTSSignature(parameters: [Parameter], returnType: BridgeType) -> String { - return "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnType.tsType)" + private func renderTSSignature(parameters: [Parameter], returnType: BridgeType, effects: Effects) -> String { + let returnTypeWithEffect: String + if effects.isAsync { + returnTypeWithEffect = "Promise<\(returnType.tsType)>" + } else { + returnTypeWithEffect = returnType.tsType + } + return "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnTypeWithEffect)" } func renderExportedFunction(function: ExportedFunction) -> (js: [String], dts: [String]) { @@ -315,7 +332,7 @@ struct BridgeJSLink { ) var dtsLines: [String] = [] dtsLines.append( - "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));" + "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" ) return (funcLines, dtsLines) @@ -346,7 +363,7 @@ struct BridgeJSLink { jsLines.append(contentsOf: funcLines.map { $0.indent(count: 4) }) dtsExportEntryLines.append( - "new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));" + "new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name), effects: constructor.effects));" .indent(count: 4) ) } @@ -368,7 +385,7 @@ struct BridgeJSLink { ).map { $0.indent(count: 4) } ) dtsTypeLines.append( - "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));" + "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" .indent(count: 4) ) } @@ -422,7 +439,7 @@ struct BridgeJSLink { } func call(name: String, returnType: BridgeType) { - let call = "options.imports.\(name)(\(parameterForwardings.joined(separator: ", ")))" + let call = "imports.\(name)(\(parameterForwardings.joined(separator: ", ")))" if returnType == .void { bodyLines.append("\(call);") } else { @@ -431,7 +448,7 @@ struct BridgeJSLink { } func callConstructor(name: String) { - let call = "new options.imports.\(name)(\(parameterForwardings.joined(separator: ", ")))" + let call = "new imports.\(name)(\(parameterForwardings.joined(separator: ", ")))" bodyLines.append("let ret = \(call);") } @@ -508,9 +525,10 @@ struct BridgeJSLink { name: function.abiName(context: nil), returnExpr: returnExpr ) + let effects = Effects(isAsync: false, isThrows: false) importObjectBuilder.appendDts( [ - "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));" + "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: effects));" ] ) importObjectBuilder.assignToImportObject(name: function.abiName(context: nil), function: funcLines) @@ -584,7 +602,8 @@ struct BridgeJSLink { importObjectBuilder.assignToImportObject(name: abiName, function: funcLines) importObjectBuilder.appendDts([ "\(type.name): {", - "new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType));".indent(count: 4), + "new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType, effects: Effects(isAsync: false, isThrows: false)));" + .indent(count: 4), "}", ]) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift index 47a7a0fa..c31688bf 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift @@ -397,7 +397,9 @@ class ExportSwift { var callExpr: ExprSyntax = "\(raw: callee)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))" if effects.isAsync { - callExpr = ExprSyntax(AwaitExprSyntax(awaitKeyword: .keyword(.await), expression: callExpr)) + callExpr = ExprSyntax( + AwaitExprSyntax(awaitKeyword: .keyword(.await).with(\.trailingTrivia, .space), expression: callExpr) + ) } if effects.isThrows { callExpr = ExprSyntax( @@ -407,6 +409,12 @@ class ExportSwift { ) ) } + + + if effects.isAsync, returnType != .void { + return StmtSyntax("return \(raw: callExpr).jsValue") + } + let retMutability = returnType == .string ? "var" : "let" if returnType == .void { return StmtSyntax("\(raw: callExpr)") @@ -430,6 +438,15 @@ class ExportSwift { } func lowerReturnValue(returnType: BridgeType) { + if effects.isAsync { + // Async functions always return a Promise, which is a JSObject + _lowerReturnValue(returnType: .jsObject(nil)) + } else { + _lowerReturnValue(returnType: returnType) + } + } + + private func _lowerReturnValue(returnType: BridgeType) { switch returnType { case .void: abiReturnType = nil @@ -450,6 +467,12 @@ class ExportSwift { abiReturnType = .pointer } + if effects.isAsync { + // The return value of async function (T of `(...) async -> T`) is + // handled by the JSPromise.async, so we don't need to do anything here. + return + } + switch returnType { case .void: break case .int, .float, .double: @@ -489,7 +512,14 @@ class ExportSwift { func render(abiName: String) -> DeclSyntax { let body: CodeBlockItemListSyntax - if effects.isThrows { + if effects.isAsync { + body = """ + let ret = JSPromise.async { + \(CodeBlockItemListSyntax(self.body)) + }.jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + """ + } else if effects.isThrows { body = """ do { \(CodeBlockItemListSyntax(self.body)) diff --git a/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts b/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts index e1daa4af..b53e2420 100644 --- a/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts +++ b/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts @@ -12,10 +12,15 @@ export type Parameter = { type: BridgeType; } +export type Effects = { + isAsync: boolean; +} + export type ImportFunctionSkeleton = { name: string; parameters: Parameter[]; returnType: BridgeType; + effects: Effects; documentation: string | undefined; } diff --git a/Plugins/BridgeJS/Sources/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/JavaScript/src/processor.js index 0f97ea14..aeaf6a2d 100644 --- a/Plugins/BridgeJS/Sources/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/JavaScript/src/processor.js @@ -162,6 +162,7 @@ export class TypeProcessor { parameters, returnType: bridgeReturnType, documentation, + effects: { isAsync: false }, }; } @@ -341,6 +342,10 @@ export class TypeProcessor { * @private */ visitType(type, node) { + // Treat A and A as the same type + if (isTypeReference(type)) { + type = type.target; + } const maybeProcessed = this.processedTypes.get(type); if (maybeProcessed) { return maybeProcessed; @@ -364,8 +369,13 @@ export class TypeProcessor { "object": { "jsObject": {} }, "symbol": { "jsObject": {} }, "never": { "void": {} }, + "Promise": { + "jsObject": { + "_0": "JSPromise" + } + }, }; - const typeString = this.checker.typeToString(type); + const typeString = type.getSymbol()?.name ?? this.checker.typeToString(type); if (typeMap[typeString]) { return typeMap[typeString]; } @@ -377,7 +387,7 @@ export class TypeProcessor { if (this.checker.isTypeAssignableTo(type, this.checker.getStringType())) { return { "string": {} }; } - if (type.getFlags() & ts.TypeFlags.TypeParameter) { + if (type.isTypeParameter()) { return { "jsObject": {} }; } @@ -412,3 +422,24 @@ export class TypeProcessor { return undefined; } } + +/** + * @param {ts.Type} type + * @returns {type is ts.ObjectType} + */ +function isObjectType(type) { + // @ts-ignore + return typeof type.objectFlags === "number"; +} + +/** + * + * @param {ts.Type} type + * @returns {type is ts.TypeReference} + */ +function isTypeReference(type) { + return ( + isObjectType(type) && + (type.objectFlags & ts.ObjectFlags.Reference) !== 0 + ); +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.d.ts new file mode 100644 index 00000000..cb0d0cef --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.d.ts @@ -0,0 +1,7 @@ +export function asyncReturnVoid(): Promise; +export function asyncRoundTripInt(v: number): Promise; +export function asyncRoundTripString(v: string): Promise; +export function asyncRoundTripBool(v: boolean): Promise; +export function asyncRoundTripFloat(v: number): Promise; +export function asyncRoundTripDouble(v: number): Promise; +export function asyncRoundTripJSObject(v: any): Promise; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.swift new file mode 100644 index 00000000..214331b3 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Async.swift @@ -0,0 +1,19 @@ +@JS func asyncReturnVoid() async {} +@JS func asyncRoundTripInt(_ v: Int) async -> Int { + return v +} +@JS func asyncRoundTripString(_ v: String) async -> String { + return v +} +@JS func asyncRoundTripBool(_ v: Bool) async -> Bool { + return v +} +@JS func asyncRoundTripFloat(_ v: Float) async -> Float { + return v +} +@JS func asyncRoundTripDouble(_ v: Double) async -> Double { + return v +} +@JS func asyncRoundTripJSObject(_ v: JSObject) async -> JSObject { + return v +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.d.ts new file mode 100644 index 00000000..aecab090 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.d.ts @@ -0,0 +1,24 @@ +// 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 = { + asyncReturnVoid(): Promise; + asyncRoundTripInt(v: number): Promise; + asyncRoundTripString(v: string): Promise; + asyncRoundTripBool(v: boolean): Promise; + asyncRoundTripFloat(v: number): Promise; + asyncRoundTripDouble(v: number): Promise; + asyncRoundTripJSObject(v: any): Promise; +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js new file mode 100644 index 00000000..bf14507a --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js @@ -0,0 +1,107 @@ +// 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; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + return { + /** @param {WebAssembly.Imports} importObject */ + addImports: (importObject) => { + const bjs = {}; + importObject["bjs"] = bjs; + bjs["return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["make_jsstring"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["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); + } + + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + + return { + asyncReturnVoid: function bjs_asyncReturnVoid() { + const retId = instance.exports.bjs_asyncReturnVoid(); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + asyncRoundTripInt: function bjs_asyncRoundTripInt(v) { + const retId = instance.exports.bjs_asyncRoundTripInt(v); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + asyncRoundTripString: function bjs_asyncRoundTripString(v) { + const vBytes = textEncoder.encode(v); + const vId = swift.memory.retain(vBytes); + const retId = instance.exports.bjs_asyncRoundTripString(vId, vBytes.length); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + swift.memory.release(vId); + return ret; + }, + asyncRoundTripBool: function bjs_asyncRoundTripBool(v) { + const retId = instance.exports.bjs_asyncRoundTripBool(v); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + asyncRoundTripFloat: function bjs_asyncRoundTripFloat(v) { + const retId = instance.exports.bjs_asyncRoundTripFloat(v); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + asyncRoundTripDouble: function bjs_asyncRoundTripDouble(v) { + const retId = instance.exports.bjs_asyncRoundTripDouble(v); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + asyncRoundTripJSObject: function bjs_asyncRoundTripJSObject(v) { + const retId = instance.exports.bjs_asyncRoundTripJSObject(swift.memory.retain(v)); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + }, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.d.ts new file mode 100644 index 00000000..dea0bd18 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.d.ts @@ -0,0 +1,24 @@ +// 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(): JSPromise; + asyncRoundTripInt(v: number): JSPromise; + asyncRoundTripString(v: string): JSPromise; + asyncRoundTripBool(v: boolean): JSPromise; + asyncRoundTripFloat(v: number): JSPromise; + asyncRoundTripDouble(v: number): JSPromise; + asyncRoundTripJSObject(v: any): JSPromise; +} +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/Async.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js new file mode 100644 index 00000000..b901d8a4 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js @@ -0,0 +1,93 @@ +// 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; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + return { + /** @param {WebAssembly.Imports} importObject */ + addImports: (importObject) => { + const bjs = {}; + importObject["bjs"] = bjs; + bjs["return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["make_jsstring"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["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); + } + const TestModule = importObject["TestModule"] = {}; + TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid() { + let ret = options.imports.asyncReturnVoid(); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripInt"] = function bjs_asyncRoundTripInt(v) { + let ret = options.imports.asyncRoundTripInt(v); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripString"] = function bjs_asyncRoundTripString(v) { + const vObject = swift.memory.getObject(v); + swift.memory.release(v); + let ret = options.imports.asyncRoundTripString(vObject); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripBool"] = function bjs_asyncRoundTripBool(v) { + let ret = options.imports.asyncRoundTripBool(v); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripFloat"] = function bjs_asyncRoundTripFloat(v) { + let ret = options.imports.asyncRoundTripFloat(v); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripDouble"] = function bjs_asyncRoundTripDouble(v) { + let ret = options.imports.asyncRoundTripDouble(v); + return swift.memory.retain(ret); + } + TestModule["bjs_asyncRoundTripJSObject"] = function bjs_asyncRoundTripJSObject(v) { + let ret = options.imports.asyncRoundTripJSObject(swift.memory.getObject(v)); + return swift.memory.retain(ret); + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + + return { + + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json new file mode 100644 index 00000000..97acb66b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json @@ -0,0 +1,167 @@ +{ + "classes" : [ + + ], + "functions" : [ + { + "abiName" : "bjs_asyncReturnVoid", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncReturnVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripInt", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripInt", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripString", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripString", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripBool", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripBool", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripFloat", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripFloat", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "float" : { + + } + } + } + ], + "returnType" : { + "float" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripDouble", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripDouble", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripJSObject", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripJSObject", + "parameters" : [ + { + "label" : "_", + "name" : "v", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + + } + } + } + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.swift new file mode 100644 index 00000000..493d4530 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.swift @@ -0,0 +1,114 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +@_spi(JSObject_id) import JavaScriptKit + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "return_string") +private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) +@_extern(wasm, module: "bjs", name: "init_memory") +private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) + +@_extern(wasm, module: "bjs", name: "swift_js_retain") +private func _swift_js_retain(_ ptr: Int32) -> Int32 +@_extern(wasm, module: "bjs", name: "swift_js_throw") +private func _swift_js_throw(_ id: Int32) +#endif + +@_expose(wasm, "bjs_asyncReturnVoid") +@_cdecl("bjs_asyncReturnVoid") +public func _bjs_asyncReturnVoid() -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + await asyncReturnVoid() + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripInt") +@_cdecl("bjs_asyncRoundTripInt") +public func _bjs_asyncRoundTripInt(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let ret = await asyncRoundTripInt(_: Int(v)) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripString") +@_cdecl("bjs_asyncRoundTripString") +public func _bjs_asyncRoundTripString(vBytes: Int32, vLen: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let v = String(unsafeUninitializedCapacity: Int(vLen)) { b in + _init_memory(vBytes, b.baseAddress.unsafelyUnwrapped) + return Int(vLen) + } + var ret = await asyncRoundTripString(_: v) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripBool") +@_cdecl("bjs_asyncRoundTripBool") +public func _bjs_asyncRoundTripBool(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let ret = await asyncRoundTripBool(_: v == 1) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripFloat") +@_cdecl("bjs_asyncRoundTripFloat") +public func _bjs_asyncRoundTripFloat(v: Float32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let ret = await asyncRoundTripFloat(_: v) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripDouble") +@_cdecl("bjs_asyncRoundTripDouble") +public func _bjs_asyncRoundTripDouble(v: Float64) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let ret = await asyncRoundTripDouble(_: v) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripJSObject") +@_cdecl("bjs_asyncRoundTripJSObject") +public func _bjs_asyncRoundTripJSObject(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let ret = await asyncRoundTripJSObject(_: JSObject(id: UInt32(bitPattern: v))) + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Async.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Async.swift new file mode 100644 index 00000000..e11ebfa9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Async.swift @@ -0,0 +1,120 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +@_spi(JSObject_id) import JavaScriptKit + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_jsstring") +func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 +#else +func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "init_memory_with_result") +func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) +#else +func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) { + fatalError("Only available on WebAssembly") +} +#endif + +func asyncReturnVoid() -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncReturnVoid") + func bjs_asyncReturnVoid() -> Int32 + #else + func bjs_asyncReturnVoid() -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncReturnVoid() + return JSPromise(takingThis: ret) +} + +func asyncRoundTripInt(_ v: Double) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripInt") + func bjs_asyncRoundTripInt(_ v: Float64) -> Int32 + #else + func bjs_asyncRoundTripInt(_ v: Float64) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncRoundTripInt(v) + return JSPromise(takingThis: ret) +} + +func asyncRoundTripString(_ v: String) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripString") + func bjs_asyncRoundTripString(_ v: Int32) -> Int32 + #else + func bjs_asyncRoundTripString(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + var v = v + let vId = v.withUTF8 { b in + _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) + } + let ret = bjs_asyncRoundTripString(vId) + return JSPromise(takingThis: ret) +} + +func asyncRoundTripBool(_ v: Bool) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripBool") + func bjs_asyncRoundTripBool(_ v: Int32) -> Int32 + #else + func bjs_asyncRoundTripBool(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncRoundTripBool(Int32(v ? 1 : 0)) + return JSPromise(takingThis: ret) +} + +func asyncRoundTripFloat(_ v: Double) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripFloat") + func bjs_asyncRoundTripFloat(_ v: Float64) -> Int32 + #else + func bjs_asyncRoundTripFloat(_ v: Float64) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncRoundTripFloat(v) + return JSPromise(takingThis: ret) +} + +func asyncRoundTripDouble(_ v: Double) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripDouble") + func bjs_asyncRoundTripDouble(_ v: Float64) -> Int32 + #else + func bjs_asyncRoundTripDouble(_ v: Float64) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncRoundTripDouble(v) + return JSPromise(takingThis: ret) +} + +func asyncRoundTripJSObject(_ v: JSObject) -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_asyncRoundTripJSObject") + func bjs_asyncRoundTripJSObject(_ v: Int32) -> Int32 + #else + func bjs_asyncRoundTripJSObject(_ v: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_asyncRoundTripJSObject(Int32(bitPattern: v.id)) + return JSPromise(takingThis: ret) +} \ No newline at end of file diff --git a/Plugins/PackageToJS/Templates/instantiate.d.ts b/Plugins/PackageToJS/Templates/instantiate.d.ts index e42e4f2f..8c5f885d 100644 --- a/Plugins/PackageToJS/Templates/instantiate.d.ts +++ b/Plugins/PackageToJS/Templates/instantiate.d.ts @@ -67,9 +67,13 @@ export type InstantiateOptions = { module: ModuleSource, /* #if HAS_IMPORTS */ /** - * The imports provided by the embedder + * The function to get the imports provided by the embedder */ - imports: Imports, + getImports: (importsContext: { + getInstance: () => WebAssembly.Instance | null, + getExports: () => Exports | null, + _swift: SwiftRuntime, + }) => Imports, /* #endif */ /* #if IS_WASI */ /** diff --git a/Plugins/PackageToJS/Templates/instantiate.js b/Plugins/PackageToJS/Templates/instantiate.js index 65996d86..5d6fe6b9 100644 --- a/Plugins/PackageToJS/Templates/instantiate.js +++ b/Plugins/PackageToJS/Templates/instantiate.js @@ -23,8 +23,11 @@ import { createInstantiator } from "./bridge-js.js" */ async function createInstantiator(options, swift) { return { - /** @param {WebAssembly.Imports} importObject */ - addImports: (importObject) => {}, + /** + * @param {WebAssembly.Imports} importObject + * @param {unknown} importsContext + */ + addImports: (importObject, importsContext) => {}, /** @param {WebAssembly.Instance} instance */ setInstance: (instance) => {}, /** @param {WebAssembly.Instance} instance */ @@ -93,12 +96,13 @@ async function _instantiate( /* #endif */ /* #endif */ }; - instantiator.addImports(importObject); - options.addToCoreImports?.(importObject, { + const importsContext = { getInstance: () => instance, getExports: () => exports, _swift: swift, - }); + }; + instantiator.addImports(importObject, importsContext); + options.addToCoreImports?.(importObject, importsContext); let module; let instance; diff --git a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift index 24a9ae48..fa60d426 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSPromise.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSPromise.swift @@ -23,6 +23,10 @@ public final class JSPromise: JSBridgedClass { self.init(from: jsObject) } + @_spi(JSObject_id) public convenience init(takingThis: Int32) { + self.init(unsafelyWrapping: JSObject(id: UInt32(bitPattern: takingThis))) + } + /// Creates a new `JSPromise` instance from a given JavaScript `Promise` object. If `value` /// is not an object and is not an instance of JavaScript `Promise`, this function will /// return `nil`. @@ -66,6 +70,33 @@ public final class JSPromise: JSBridgedClass { self.init(unsafelyWrapping: Self.constructor!.new(closure)) } + public static func async(body: @escaping () async throws(JSException) -> Void) -> JSPromise { + self.async { () throws(JSException) -> JSValue in + try await body() + return .undefined + } + } + public static func async(body: @escaping () async throws(JSException) -> JSValue) -> JSPromise { + JSPromise { resolver in + // NOTE: The context is fully transferred to the unstructured task + // isolation but the compiler can't prove it yet, so we need to + // use `@unchecked Sendable` to make it compile with the Swift 6 mode. + struct Context: @unchecked Sendable { + let resolver: (JSPromise.Result) -> Void + let body: () async throws(JSException) -> JSValue + } + let context = Context(resolver: resolver, body: body) + Task { + do throws(JSException) { + let result = try await context.body() + context.resolver(.success(result)) + } catch { + context.resolver(.failure(error.thrownValue)) + } + } + } + } + #if !hasFeature(Embedded) public static func resolve(_ value: ConvertibleToJSValue) -> JSPromise { self.init(unsafelyWrapping: Self.constructor!.resolve!(value).object!) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift index 18a40078..0a869882 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift @@ -160,24 +160,16 @@ private func makeAsyncClosure( _ body: sending @escaping (sending [JSValue]) async throws(JSException) -> JSValue ) -> ((sending [JSValue]) -> JSValue) { { arguments in - JSPromise { resolver in - // NOTE: The context is fully transferred to the unstructured task - // isolation but the compiler can't prove it yet, so we need to - // use `@unchecked Sendable` to make it compile with the Swift 6 mode. - struct Context: @unchecked Sendable { - let resolver: (JSPromise.Result) -> Void - let arguments: [JSValue] - let body: (sending [JSValue]) async throws(JSException) -> JSValue - } - let context = Context(resolver: resolver, arguments: arguments, body: body) - Task { - do throws(JSException) { - let result = try await context.body(context.arguments) - context.resolver(.success(result)) - } catch { - context.resolver(.failure(error.thrownValue)) - } - } + // NOTE: The context is fully transferred to the unstructured task + // isolation but the compiler can't prove it yet, so we need to + // use `@unchecked Sendable` to make it compile with the Swift 6 mode. + struct Context: @unchecked Sendable { + let arguments: [JSValue] + let body: (sending [JSValue]) async throws(JSException) -> JSValue + } + let context = Context(arguments: arguments, body: body) + return JSPromise.async { () async throws(JSException) -> JSValue in + try await context.body(context.arguments) }.jsValue() } } diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 2b78b96b..b6c359df 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -1,5 +1,6 @@ import XCTest import JavaScriptKit +import JavaScriptEventLoop @_extern(wasm, module: "BridgeJSRuntimeTests", name: "runJsWorks") @_extern(c) @@ -49,6 +50,15 @@ struct TestError: Error { @JS func throwsWithSwiftHeapObjectResult() throws(JSException) -> Greeter { return Greeter(name: "Test") } @JS func throwsWithJSObjectResult() throws(JSException) -> JSObject { return JSObject() } +@JS func asyncRoundTripVoid() async -> Void { return } +@JS func asyncRoundTripInt(v: Int) async -> Int { return v } +@JS func asyncRoundTripFloat(v: Float) async -> Float { return v } +@JS func asyncRoundTripDouble(v: Double) async -> Double { return v } +@JS func asyncRoundTripBool(v: Bool) async -> Bool { return v } +@JS func asyncRoundTripString(v: String) async -> String { return v } +@JS func asyncRoundTripSwiftHeapObject(v: Greeter) async -> Greeter { return v } +@JS func asyncRoundTripJSObject(v: JSObject) async -> JSObject { return v } + @JS class Greeter { var name: String @@ -83,4 +93,8 @@ class ExportAPITests: XCTestCase { runJsWorks() XCTAssertTrue(hasDeinitGreeter) } + + func testAllAsync() async throws { + _ = try await runAsyncWorks().value() + } } diff --git a/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift index 363bf2d9..2f09308a 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift @@ -312,6 +312,114 @@ public func _bjs_throwsWithJSObjectResult() -> Int32 { #endif } +@_expose(wasm, "bjs_asyncRoundTripVoid") +@_cdecl("bjs_asyncRoundTripVoid") +public func _bjs_asyncRoundTripVoid() -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + await asyncRoundTripVoid() + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripInt") +@_cdecl("bjs_asyncRoundTripInt") +public func _bjs_asyncRoundTripInt(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripInt(v: Int(v)).jsValue + }.jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripFloat") +@_cdecl("bjs_asyncRoundTripFloat") +public func _bjs_asyncRoundTripFloat(v: Float32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripFloat(v: v).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripDouble") +@_cdecl("bjs_asyncRoundTripDouble") +public func _bjs_asyncRoundTripDouble(v: Float64) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripDouble(v: v).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripBool") +@_cdecl("bjs_asyncRoundTripBool") +public func _bjs_asyncRoundTripBool(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripBool(v: v == 1).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripString") +@_cdecl("bjs_asyncRoundTripString") +public func _bjs_asyncRoundTripString(vBytes: Int32, vLen: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + let v = String(unsafeUninitializedCapacity: Int(vLen)) { b in + _init_memory(vBytes, b.baseAddress.unsafelyUnwrapped) + return Int(vLen) + } + return await asyncRoundTripString(v: v).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripSwiftHeapObject") +@_cdecl("bjs_asyncRoundTripSwiftHeapObject") +public func _bjs_asyncRoundTripSwiftHeapObject(v: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripSwiftHeapObject(v: Unmanaged.fromOpaque(v).takeUnretainedValue()).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_asyncRoundTripJSObject") +@_cdecl("bjs_asyncRoundTripJSObject") +public func _bjs_asyncRoundTripJSObject(v: Int32) -> Int32 { + #if arch(wasm32) + let ret = JSPromise.async { + return await asyncRoundTripJSObject(v: JSObject(id: UInt32(bitPattern: v))).jsValue + } .jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_takeGreeter") @_cdecl("bjs_takeGreeter") public func _bjs_takeGreeter(g: UnsafeMutableRawPointer, nameBytes: Int32, nameLen: Int32) -> Void { diff --git a/Tests/BridgeJSRuntimeTests/Generated/ImportTS.swift b/Tests/BridgeJSRuntimeTests/Generated/ImportTS.swift index 35148cf5..fa92ac62 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/ImportTS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/ImportTS.swift @@ -82,6 +82,19 @@ func jsRoundTripString(_ v: String) -> String { } } +func runAsyncWorks() -> JSPromise { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_runAsyncWorks") + func bjs_runAsyncWorks() -> Int32 + #else + func bjs_runAsyncWorks() -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_runAsyncWorks() + return JSPromise(takingThis: ret) +} + struct JsGreeter { let this: JSObject diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json index 7a467cc3..9282ab57 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json @@ -385,6 +385,190 @@ } } }, + { + "abiName" : "bjs_asyncRoundTripVoid", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripInt", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripInt", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripFloat", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripFloat", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "float" : { + + } + } + } + ], + "returnType" : { + "float" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripDouble", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripDouble", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripBool", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripBool", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripString", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripString", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripSwiftHeapObject", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripSwiftHeapObject", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + }, + { + "abiName" : "bjs_asyncRoundTripJSObject", + "effects" : { + "isAsync" : true, + "isThrows" : false + }, + "name" : "asyncRoundTripJSObject", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + + } + } + }, { "abiName" : "bjs_takeGreeter", "effects" : { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ImportTS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ImportTS.json index ad8fcd87..2e02381d 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ImportTS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ImportTS.json @@ -66,6 +66,17 @@ } } + }, + { + "name" : "runAsyncWorks", + "parameters" : [ + + ], + "returnType" : { + "jsObject" : { + "_0" : "JSPromise" + } + } } ], "types" : [ diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 664dd447..c4dd48d6 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -9,4 +9,6 @@ export class JsGreeter { constructor(name: string, prefix: string); greet(): string; changeName(name: string): void; -} \ No newline at end of file +} + +export function runAsyncWorks(): Promise; \ No newline at end of file diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 4a28d6aa..ffc680fb 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -6,36 +6,46 @@ export async function setupOptions(options, context) { setupTestGlobals(globalThis); return { ...options, - imports: { - "jsRoundTripVoid": () => { - return; - }, - "jsRoundTripNumber": (v) => { - return v; - }, - "jsRoundTripBool": (v) => { - return v; - }, - "jsRoundTripString": (v) => { - return v; - }, - JsGreeter: class { - /** - * @param {string} name - * @param {string} prefix - */ - constructor(name, prefix) { - this.name = name; - this.prefix = prefix; - } - greet() { - return `${this.prefix}, ${this.name}!`; - } - /** @param {string} name */ - changeName(name) { - this.name = name; + getImports: (importsContext) => { + return { + "jsRoundTripVoid": () => { + return; + }, + "jsRoundTripNumber": (v) => { + return v; + }, + "jsRoundTripBool": (v) => { + return v; + }, + "jsRoundTripString": (v) => { + return v; + }, + JsGreeter: class { + /** + * @param {string} name + * @param {string} prefix + */ + constructor(name, prefix) { + this.name = name; + this.prefix = prefix; + } + greet() { + return `${this.prefix}, ${this.name}!`; + } + /** @param {string} name */ + changeName(name) { + this.name = name; + } + }, + runAsyncWorks: async () => { + const exports = importsContext.getExports(); + if (!exports) { + throw new Error("No exports!?"); + } + BridgeJSRuntimeTests_runAsyncWorks(exports); + return; } - } + }; }, addToCoreImports(importObject, importsContext) { const { getInstance, getExports } = importsContext; @@ -45,7 +55,11 @@ export async function setupOptions(options, context) { } const bridgeJSRuntimeTests = importObject["BridgeJSRuntimeTests"] || {}; bridgeJSRuntimeTests["runJsWorks"] = () => { - return BridgeJSRuntimeTests_runJsWorks(getInstance(), getExports()); + const exports = getExports(); + if (!exports) { + throw new Error("No exports!?"); + } + return BridgeJSRuntimeTests_runJsWorks(getInstance(), exports); } importObject["BridgeJSRuntimeTests"] = bridgeJSRuntimeTests; } @@ -120,6 +134,11 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { } } +/** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ +async function BridgeJSRuntimeTests_runAsyncWorks(exports) { + await exports.asyncRoundTripVoid(); +} + function setupTestGlobals(global) { global.globalObject1 = { prop_1: { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy