From 43a05d9ad7e77801f6dc1f4a163bb126edcc8460 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 4 Feb 2026 09:50:08 +0100 Subject: [PATCH] BridgeJS: Few more array import tests BridgeJS: Array import --- .../Sources/BridgeJSCore/ImportTS.swift | 88 +-- .../Sources/BridgeJSLink/BridgeJSLink.swift | 8 +- .../Sources/BridgeJSLink/JSGlueGen.swift | 56 +- .../TS2Swift/JavaScript/src/processor.js | 11 +- .../test/__snapshots__/ts2swift.test.js.snap | 12 +- .../test/fixtures/ArrayParameter.d.ts | 20 +- .../Inputs/MacroSwift/ArrayTypes.swift | 6 + .../Inputs/MacroSwift/ImportArray.swift | 2 + .../BridgeJSCodegenTests/ArrayTypes.json | 115 +++ .../BridgeJSCodegenTests/ArrayTypes.swift | 88 +++ .../BridgeJSCodegenTests/ImportArray.json | 62 ++ .../BridgeJSCodegenTests/ImportArray.swift | 34 + .../BridgeJSLinkTests/ArrayTypes.d.ts | 5 + .../BridgeJSLinkTests/ArrayTypes.js | 89 +++ .../BridgeJSLinkTests/ImportArray.d.ts | 19 + .../BridgeJSLinkTests/ImportArray.js | 257 +++++++ .../JavaScriptKit/BridgeJSIntrinsics.swift | 23 +- .../Articles/BridgeJS/Supported-Types.md | 2 +- .../Generated/BridgeJS.Macros.swift | 10 + .../Generated/BridgeJS.swift | 407 +++++++++++ .../Generated/JavaScript/BridgeJS.json | 663 ++++++++++++++++-- .../BridgeJSRuntimeTests/ImportAPITests.swift | 61 ++ .../ImportArrayAPIs.swift | 17 + Tests/BridgeJSRuntimeTests/StructAPIs.swift | 25 + Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 6 + Tests/prelude.mjs | 50 ++ 26 files changed, 2011 insertions(+), 125 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ImportArray.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js create mode 100644 Tests/BridgeJSRuntimeTests/ImportArrayAPIs.swift diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 22e0ef162..8db872c6e 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -83,6 +83,8 @@ public struct ImportTS { var 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 + var stackLoweringStmts: [CodeBlockItemSyntax] = [] init(moduleName: String, abiName: String, context: BridgeContext = .importTS) { self.moduleName = moduleName @@ -93,11 +95,6 @@ public struct ImportTS { func lowerParameter(param: Parameter) throws { let loweringInfo = try param.type.loweringParameterInfo(context: context) - // Generate destructured variable names for all lowered parameters - let destructuredNames = loweringInfo.loweredParameters.map { - "\(param.name)\($0.name.capitalizedFirstLetter)" - } - let initializerExpr: ExprSyntax switch param.type { case .closure(let signature): @@ -108,6 +105,33 @@ public struct ImportTS { initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") } + if loweringInfo.loweredParameters.isEmpty { + let stmt = CodeBlockItemSyntax( + item: .decl( + DeclSyntax( + VariableDeclSyntax( + bindingSpecifier: .keyword(.let), + bindings: PatternBindingListSyntax { + PatternBindingSyntax( + pattern: PatternSyntax( + IdentifierPatternSyntax(identifier: .wildcardToken()) + ), + initializer: InitializerClauseSyntax(value: initializerExpr) + ) + } + ) + ) + ) + ) + stackLoweringStmts.insert(stmt, at: 0) + return + } + + // Generate destructured variable names for all lowered parameters + let destructuredNames = loweringInfo.loweredParameters.map { + "\(param.name)\($0.name.capitalizedFirstLetter)" + } + // Always add destructuring statement to body (unified for single and multiple) let pattern: PatternSyntax if destructuredNames.count == 1 { @@ -166,7 +190,9 @@ public struct ImportTS { } func call(returnType: BridgeType) throws { - // Build function call expression + let liftingInfo = try returnType.liftingReturnInfo(context: context) + body.append(contentsOf: stackLoweringStmts) + let callExpr = FunctionCallExprSyntax( calledExpression: ExprSyntax("\(raw: abiName)"), leftParen: .leftParenToken(), @@ -178,19 +204,15 @@ public struct ImportTS { rightParen: .rightParenToken() ) - let needsRetBinding: Bool - let liftingInfo = try returnType.liftingReturnInfo(context: context) - if liftingInfo.valueToLift == nil || returnType.usesSideChannelForOptionalReturn() { - // Void and side-channel returns don't need "let ret =" - needsRetBinding = false + if returnType == .void { + body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr))))) + } else if returnType.usesSideChannelForOptionalReturn() { + // Side channel returns don't need "let ret =" + body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr))))) + } else if liftingInfo.valueToLift == nil { + body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr))))) } else { - needsRetBinding = true - } - - if needsRetBinding { body.append("let ret = \(raw: callExpr)") - } else { - body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr))))) } // Add exception check for ImportTS context @@ -934,13 +956,8 @@ extension BridgeType { return LoweringParameterInfo(loweredParameters: [("value", .i32)]) } case .rawValueEnum(_, let rawType): - switch context { - case .importTS: - return LoweringParameterInfo(loweredParameters: [("value", rawType.wasmCoreType ?? .i32)]) - case .exportSwift: - // For protocol export we return .i32 for String raw value type instead of nil - return LoweringParameterInfo(loweredParameters: [("value", rawType.wasmCoreType ?? .i32)]) - } + let wasmType = rawType.wasmCoreType ?? .i32 + return LoweringParameterInfo(loweredParameters: [("value", wasmType)]) case .associatedValueEnum: switch context { case .importTS: @@ -964,12 +981,7 @@ extension BridgeType { params.append(contentsOf: wrappedInfo.loweredParameters) return LoweringParameterInfo(loweredParameters: params) case .array: - switch context { - case .importTS: - throw BridgeJSCoreError("Array types are not yet supported in TypeScript imports") - case .exportSwift: - return LoweringParameterInfo(loweredParameters: []) - } + return LoweringParameterInfo(loweredParameters: []) } } @@ -1025,13 +1037,8 @@ extension BridgeType { return LiftingReturnInfo(valueToLift: .i32) } case .rawValueEnum(_, let rawType): - switch context { - case .importTS: - return LiftingReturnInfo(valueToLift: rawType.wasmCoreType ?? .i32) - case .exportSwift: - // For protocol export we return .i32 for String raw value type instead of nil - return LiftingReturnInfo(valueToLift: rawType.wasmCoreType ?? .i32) - } + let wasmType = rawType.wasmCoreType ?? .i32 + return LiftingReturnInfo(valueToLift: wasmType) case .associatedValueEnum: switch context { case .importTS: @@ -1053,12 +1060,7 @@ extension BridgeType { let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) case .array: - switch context { - case .importTS: - throw BridgeJSCoreError("Array types are not yet supported in TypeScript imports") - case .exportSwift: - return LiftingReturnInfo(valueToLift: nil) - } + return LiftingReturnInfo(valueToLift: nil) } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 27ef60760..7a5e02f2d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -2212,12 +2212,10 @@ extension BridgeJSLink { func liftParameter(param: Parameter) throws { let liftingFragment = try IntrinsicJSFragment.liftParameter(type: param.type, context: context) - assert( - liftingFragment.parameters.count >= 1, - "Lifting fragment should have at least one parameter to lift" - ) let valuesToLift: [String] - if liftingFragment.parameters.count == 1 { + if liftingFragment.parameters.count == 0 { + valuesToLift = [] + } else if liftingFragment.parameters.count == 1 { parameterNames.append(param.name) valuesToLift = [scope.variable(param.name)] } else { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 1547d63a4..410bce672 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -1895,14 +1895,7 @@ struct IntrinsicJSFragment: Sendable { "Namespace enums are not supported to be passed as parameters to imported JS functions: \(string)" ) case .array(let elementType): - switch context { - case .importTS: - throw BridgeJSLinkError( - message: "Arrays are not yet supported to be passed as parameters to imported JS functions" - ) - case .exportSwift: - return try arrayLift(elementType: elementType) - } + return try arrayLift(elementType: elementType) } } @@ -1971,14 +1964,7 @@ struct IntrinsicJSFragment: Sendable { message: "Namespace enums are not supported to be returned from imported JS functions: \(string)" ) case .array(let elementType): - switch context { - case .importTS: - throw BridgeJSLinkError( - message: "Arrays are not yet supported to be returned from imported JS functions" - ) - case .exportSwift: - return try arrayLower(elementType: elementType) - } + return try arrayLower(elementType: elementType) } } @@ -3215,11 +3201,10 @@ struct IntrinsicJSFragment: Sendable { // Lift return value if needed if method.returnType != .void { let liftFragment = try! IntrinsicJSFragment.liftReturn(type: method.returnType) - if !liftFragment.parameters.isEmpty { - let lifted = liftFragment.printCode(["ret"], methodScope, printer, methodCleanup) - if let liftedValue = lifted.first { - printer.write("return \(liftedValue);") - } + let liftArgs = liftFragment.parameters.isEmpty ? [] : ["ret"] + let lifted = liftFragment.printCode(liftArgs, methodScope, printer, methodCleanup) + if let liftedValue = lifted.first { + printer.write("return \(liftedValue);") } } } @@ -3508,9 +3493,36 @@ struct IntrinsicJSFragment: Sendable { ), allStructs: allStructs ) + let guardedPrinter = CodeFragmentPrinter() + let guardedCleanup = CodeFragmentPrinter() + _ = wrappedFragment.printCode([value], scope, guardedPrinter, guardedCleanup) + var loweredLines = guardedPrinter.lines + var hoistedCleanupVar: String? + if let first = loweredLines.first { + let trimmed = first.trimmingCharacters(in: .whitespaces) + if trimmed.hasPrefix("const "), + let namePart = trimmed.split(separator: " ").dropFirst().first, + trimmed.contains("= []") + { + hoistedCleanupVar = String(namePart) + loweredLines[0] = "\(hoistedCleanupVar!) = [];" + } + } + if let hoistedName = hoistedCleanupVar { + printer.write("let \(hoistedName);") + } printer.write("if (\(isSomeVar)) {") printer.indent { - _ = wrappedFragment.printCode([value], scope, printer, cleanup) + for line in loweredLines { + printer.write(line) + } + if !guardedCleanup.lines.isEmpty { + cleanup.write("if (\(isSomeVar)) {") + cleanup.indent { + cleanup.write(contentsOf: guardedCleanup) + } + cleanup.write("}") + } } printer.write("}") printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js index c06ba94aa..ef446af0f 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js @@ -663,6 +663,15 @@ export class TypeProcessor { * @private */ visitType(type, node) { + if (this.checker.isArrayType(type)) { + const typeArgs = this.checker.getTypeArguments(type); + if (typeArgs && typeArgs.length > 0) { + const elementType = this.visitType(typeArgs[0], node); + return `[${elementType}]`; + } + return "[JSObject]"; + } + // Treat A and A as the same type if (isTypeReference(type)) { type = type.target; @@ -727,7 +736,7 @@ export class TypeProcessor { return this.renderTypeIdentifier(typeName); } - if (this.checker.isArrayType(type) || this.checker.isTupleType(type) || type.getCallSignatures().length > 0) { + if (this.checker.isTupleType(type) || type.getCallSignatures().length > 0) { return "JSObject"; } // "a" | "b" -> string 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 f81a2ce07..6fdb79f78 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 @@ -9,11 +9,17 @@ exports[`ts2swift > snapshots Swift output for ArrayParameter.d.ts > ArrayParame @_spi(Experimental) @_spi(BridgeJS) import JavaScriptKit -@JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void +@JSFunction func processNumbers(_ values: [Double]) throws(JSException) -> Void -@JSFunction func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void +@JSFunction func getNumbers() throws(JSException) -> [Double] -@JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void +@JSFunction func transformNumbers(_ values: [Double]) throws(JSException) -> [Double] + +@JSFunction func processStrings(_ values: [String]) throws(JSException) -> [String] + +@JSFunction func processBooleans(_ values: [Bool]) throws(JSException) -> [Bool] + +@JSFunction func processArraySyntax(_ values: [Double]) throws(JSException) -> [Double] " `; diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/ArrayParameter.d.ts b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/ArrayParameter.d.ts index 59674e071..b5a099388 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/ArrayParameter.d.ts +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/ArrayParameter.d.ts @@ -1,3 +1,17 @@ -export function checkArray(a: number[]): void; -export function checkArrayWithLength(a: number[], b: number): void; -export function checkArray(a: Array): void; +// Array as parameter +export function processNumbers(values: number[]): void; + +// Array as return value +export function getNumbers(): number[]; + +// Array as both parameter and return value +export function transformNumbers(values: number[]): number[]; + +// String arrays +export function processStrings(values: string[]): string[]; + +// Boolean arrays +export function processBooleans(values: boolean[]): boolean[]; + +// Using Array syntax +export function processArraySyntax(values: Array): Array; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ArrayTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ArrayTypes.swift index c1980cbc2..0cea90512 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ArrayTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ArrayTypes.swift @@ -61,3 +61,9 @@ @JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void @JSFunction func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void + +@JSFunction func importProcessNumbers(_ values: [Double]) throws(JSException) -> Void +@JSFunction func importGetNumbers() throws(JSException) -> [Double] +@JSFunction func importTransformNumbers(_ values: [Double]) throws(JSException) -> [Double] +@JSFunction func importProcessStrings(_ values: [String]) throws(JSException) -> [String] +@JSFunction func importProcessBooleans(_ values: [Bool]) throws(JSException) -> [Bool] diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ImportArray.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ImportArray.swift new file mode 100644 index 000000000..cd9142a26 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ImportArray.swift @@ -0,0 +1,2 @@ +@JSFunction func roundtrip(_ items: [Int]) throws(JSException) -> [Int] +@JSFunction func logStrings(_ items: [String]) throws(JSException) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json index 8dde2cfb7..8eab0c059 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json @@ -1158,6 +1158,121 @@ } } + }, + { + "name" : "importProcessNumbers", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "name" : "importGetNumbers", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "name" : "importTransformNumbers", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "name" : "importProcessStrings", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "name" : "importProcessBooleans", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } } ], "types" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift index c7c80c3b2..023caf21f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift @@ -581,4 +581,92 @@ func _$checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> V if let error = _swift_js_take_exception() { throw error } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_importProcessNumbers") +fileprivate func bjs_importProcessNumbers() -> Void +#else +fileprivate func bjs_importProcessNumbers() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$importProcessNumbers(_ values: [Double]) throws(JSException) -> Void { + let _ = values.bridgeJSLowerParameter() + bjs_importProcessNumbers() + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_importGetNumbers") +fileprivate func bjs_importGetNumbers() -> Void +#else +fileprivate func bjs_importGetNumbers() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$importGetNumbers() throws(JSException) -> [Double] { + bjs_importGetNumbers() + if let error = _swift_js_take_exception() { + throw error + } + return [Double].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_importTransformNumbers") +fileprivate func bjs_importTransformNumbers() -> Void +#else +fileprivate func bjs_importTransformNumbers() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$importTransformNumbers(_ values: [Double]) throws(JSException) -> [Double] { + let _ = values.bridgeJSLowerParameter() + bjs_importTransformNumbers() + if let error = _swift_js_take_exception() { + throw error + } + return [Double].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_importProcessStrings") +fileprivate func bjs_importProcessStrings() -> Void +#else +fileprivate func bjs_importProcessStrings() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$importProcessStrings(_ values: [String]) throws(JSException) -> [String] { + let _ = values.bridgeJSLowerParameter() + bjs_importProcessStrings() + if let error = _swift_js_take_exception() { + throw error + } + return [String].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_importProcessBooleans") +fileprivate func bjs_importProcessBooleans() -> Void +#else +fileprivate func bjs_importProcessBooleans() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$importProcessBooleans(_ values: [Bool]) throws(JSException) -> [Bool] { + let _ = values.bridgeJSLowerParameter() + bjs_importProcessBooleans() + if let error = _swift_js_take_exception() { + throw error + } + return [Bool].bridgeJSLiftReturn() } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json new file mode 100644 index 000000000..b884e0cb9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json @@ -0,0 +1,62 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + { + "name" : "roundtrip", + "parameters" : [ + { + "name" : "items", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "logStrings", + "parameters" : [ + { + "name" : "items", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "types" : [ + + ] + } + ] + }, + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.swift new file mode 100644 index 000000000..33b987c76 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.swift @@ -0,0 +1,34 @@ +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_roundtrip") +fileprivate func bjs_roundtrip() -> Void +#else +fileprivate func bjs_roundtrip() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$roundtrip(_ items: [Int]) throws(JSException) -> [Int] { + let _ = items.bridgeJSLowerParameter() + bjs_roundtrip() + if let error = _swift_js_take_exception() { + throw error + } + return [Int].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_logStrings") +fileprivate func bjs_logStrings() -> Void +#else +fileprivate func bjs_logStrings() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$logStrings(_ items: [String]) throws(JSException) -> Void { + let _ = items.bridgeJSLowerParameter() + bjs_logStrings() + if let error = _swift_js_take_exception() { + throw error + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.d.ts index 3316cd7c3..8dc76156b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.d.ts @@ -71,6 +71,11 @@ export type Exports = { export type Imports = { checkArray(a: any): void; checkArrayWithLength(a: any, b: number): void; + importProcessNumbers(values: number[]): void; + importGetNumbers(): number[]; + importTransformNumbers(values: number[]): number[]; + importProcessStrings(values: string[]): string[]; + importProcessBooleans(values: boolean[]): boolean[]; } export function createInstantiator(options: { imports: Imports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js index 4610d03ba..6cca17cf6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js @@ -263,6 +263,95 @@ export async function createInstantiator(options, swift) { setException(error); } } + TestModule["bjs_importProcessNumbers"] = function bjs_importProcessNumbers() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = tmpRetF64s.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); + imports.importProcessNumbers(arrayResult); + } catch (error) { + setException(error); + } + } + TestModule["bjs_importGetNumbers"] = function bjs_importGetNumbers() { + try { + let ret = imports.importGetNumbers(); + const arrayCleanups = []; + for (const elem of ret) { + tmpParamF64s.push(elem); + } + tmpParamInts.push(ret.length); + } catch (error) { + setException(error); + } + } + TestModule["bjs_importTransformNumbers"] = function bjs_importTransformNumbers() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const f64 = tmpRetF64s.pop(); + arrayResult.push(f64); + } + arrayResult.reverse(); + let ret = imports.importTransformNumbers(arrayResult); + const arrayCleanups = []; + for (const elem of ret) { + tmpParamF64s.push(elem); + } + tmpParamInts.push(ret.length); + } catch (error) { + setException(error); + } + } + TestModule["bjs_importProcessStrings"] = function bjs_importProcessStrings() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = tmpRetStrings.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); + let ret = imports.importProcessStrings(arrayResult); + const arrayCleanups = []; + for (const elem of ret) { + const bytes = textEncoder.encode(elem); + const id = swift.memory.retain(bytes); + tmpParamInts.push(bytes.length); + tmpParamInts.push(id); + arrayCleanups.push(() => { + swift.memory.release(id); + }); + } + tmpParamInts.push(ret.length); + } catch (error) { + setException(error); + } + } + TestModule["bjs_importProcessBooleans"] = function bjs_importProcessBooleans() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const bool = tmpRetInts.pop() !== 0; + arrayResult.push(bool); + } + arrayResult.reverse(); + let ret = imports.importProcessBooleans(arrayResult); + const arrayCleanups = []; + for (const elem of ret) { + tmpParamInts.push(elem ? 1 : 0); + } + tmpParamInts.push(ret.length); + } catch (error) { + setException(error); + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.d.ts new file mode 100644 index 000000000..5d1e2c4dc --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.d.ts @@ -0,0 +1,19 @@ +// 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 = { + roundtrip(items: number[]): number[]; + logStrings(items: string[]): void; +} +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/ImportArray.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js new file mode 100644 index 000000000..daa601aed --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js @@ -0,0 +1,257 @@ +// 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; + 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 tmpRetTag = []; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + let tmpRetPointers = []; + let tmpParamPointers = []; + let tmpStructCleanups = []; + 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) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + 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_tag"] = function(tag) { + tmpRetTag.push(tag); + } + bjs["swift_js_push_i32"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return tmpParamF64s.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + tmpRetPointers.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return tmpParamPointers.pop(); + } + bjs["swift_js_struct_cleanup"] = function(cleanupId) { + if (cleanupId === 0) { return; } + const index = (cleanupId | 0) - 1; + const cleanup = tmpStructCleanups[index]; + tmpStructCleanups[index] = null; + if (cleanup) { cleanup(); } + while (tmpStructCleanups.length > 0 && tmpStructCleanups[tmpStructCleanups.length - 1] == null) { + tmpStructCleanups.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 { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + 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; + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_roundtrip"] = function bjs_roundtrip() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = tmpRetInts.pop(); + arrayResult.push(int); + } + arrayResult.reverse(); + let ret = imports.roundtrip(arrayResult); + const arrayCleanups = []; + for (const elem of ret) { + tmpParamInts.push((elem | 0)); + } + tmpParamInts.push(ret.length); + } catch (error) { + setException(error); + } + } + TestModule["bjs_logStrings"] = function bjs_logStrings() { + try { + const arrayLen = tmpRetInts.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const string = tmpRetStrings.pop(); + arrayResult.push(string); + } + arrayResult.reverse(); + imports.logStrings(arrayResult); + } catch (error) { + setException(error); + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index e385a3c60..349cecb91 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -2297,7 +2297,9 @@ extension _BridgedAsOptional where Wrapped: _BridgedSwiftStruct { // MARK: - Array Support -extension Array where Element: _BridgedSwiftStackType, Element.StackLiftResult == Element { +extension Array: _BridgedSwiftStackType where Element: _BridgedSwiftStackType, Element.StackLiftResult == Element { + public typealias StackLiftResult = [Element] + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> [Element] { let count = Int(_swift_js_pop_i32()) var result: [Element] = [] @@ -2309,10 +2311,23 @@ extension Array where Element: _BridgedSwiftStackType, Element.StackLiftResult = return result } - @_spi(BridgeJS) public func bridgeJSLowerReturn() { - for elem in self { + @_spi(BridgeJS) public static func bridgeJSLiftReturn() -> [Element] { + bridgeJSLiftParameter() + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerStackReturn() { + bridgeJSLowerReturn() + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() { + let array = self + for elem in array { elem.bridgeJSLowerStackReturn() } - _swift_js_push_i32(Int32(self.count)) + _swift_js_push_i32(Int32(array.count)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() { + bridgeJSLowerReturn() } } diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md index 668fe21b5..c872c0dda 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md @@ -9,7 +9,7 @@ Use this page as a quick reference for how common TypeScript types appear in Swi | `number` | `Double` | | `string` | `String` | | `boolean` | `Bool` | -| TODO | `Array` | +| `T[]` | `[T]` | | TODO | `Dictionary` | | `T \| undefined` | TODO | | `T \| null` | `Optional` | diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index 358f0501d..9e05b3ae8 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -63,6 +63,16 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {} @JSFunction(jsName: "with-dashes") static func with_dashes() throws(JSException) -> StaticBox } +@JSFunction func jsRoundTripNumberArray(_ values: [Double]) throws(JSException) -> [Double] + +@JSFunction func jsRoundTripStringArray(_ values: [String]) throws(JSException) -> [String] + +@JSFunction func jsRoundTripBoolArray(_ values: [Bool]) throws(JSException) -> [Bool] + +@JSFunction func jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double + +@JSFunction func jsCreateNumberArray() throws(JSException) -> [Double] + @JSFunction(from: .global) func parseInt(_ string: String) throws(JSException) -> Double @JSClass(from: .global) struct Animal { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 0e06c7edc..5638d0442 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -3579,6 +3579,84 @@ fileprivate func _bjs_struct_lift_FooContainer() -> Int32 { } #endif +extension ArrayMembers: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter() -> ArrayMembers { + let optStrings = { + let __isSome = _swift_js_pop_i32() + if __isSome == 0 { + return Optional<[String]>.none + } else { + return [String].bridgeJSLiftParameter() + } + }() + let ints = [Int].bridgeJSLiftParameter() + return ArrayMembers(ints: ints, optStrings: optStrings) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + self.ints.bridgeJSLowerReturn() + let __bjs_isSome_optStrings = self.optStrings != nil + if let __bjs_unwrapped_optStrings = self.optStrings { + __bjs_unwrapped_optStrings.bridgeJSLowerReturn() + } + _swift_js_push_i32(__bjs_isSome_optStrings ? 1 : 0) + } + + init(unsafelyCopying jsObject: JSObject) { + let __bjs_cleanupId = _bjs_struct_lower_ArrayMembers(jsObject.bridgeJSLowerParameter()) + defer { + _swift_js_struct_cleanup(__bjs_cleanupId) + } + self = Self.bridgeJSLiftParameter() + } + + func toJSObject() -> JSObject { + let __bjs_self = self + __bjs_self.bridgeJSLowerReturn() + return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_ArrayMembers())) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_ArrayMembers") +fileprivate func _bjs_struct_lower_ArrayMembers(_ objectId: Int32) -> Int32 +#else +fileprivate func _bjs_struct_lower_ArrayMembers(_ objectId: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_ArrayMembers") +fileprivate func _bjs_struct_lift_ArrayMembers() -> Int32 +#else +fileprivate func _bjs_struct_lift_ArrayMembers() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +@_expose(wasm, "bjs_ArrayMembers_sumValues") +@_cdecl("bjs_ArrayMembers_sumValues") +public func _bjs_ArrayMembers_sumValues() -> Int32 { + #if arch(wasm32) + let ret = ArrayMembers.bridgeJSLiftParameter().sumValues(_: [Int].bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ArrayMembers_firstString") +@_cdecl("bjs_ArrayMembers_firstString") +public func _bjs_ArrayMembers_firstString() -> Void { + #if arch(wasm32) + let ret = ArrayMembers.bridgeJSLiftParameter().firstString(_: [String].bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { @@ -5913,6 +5991,43 @@ public func _bjs_roundTripFooContainer() -> Void { #endif } +@_expose(wasm, "bjs_roundTripArrayMembers") +@_cdecl("bjs_roundTripArrayMembers") +public func _bjs_roundTripArrayMembers() -> Void { + #if arch(wasm32) + let ret = roundTripArrayMembers(_: ArrayMembers.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_arrayMembersSum") +@_cdecl("bjs_arrayMembersSum") +public func _bjs_arrayMembersSum() -> Int32 { + #if arch(wasm32) + let _tmp_values = [Int].bridgeJSLiftParameter() + let _tmp_value = ArrayMembers.bridgeJSLiftParameter() + let ret = arrayMembersSum(_: _tmp_value, _: _tmp_values) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_arrayMembersFirst") +@_cdecl("bjs_arrayMembersFirst") +public func _bjs_arrayMembersFirst() -> Void { + #if arch(wasm32) + let _tmp_values = [String].bridgeJSLiftParameter() + let _tmp_value = ArrayMembers.bridgeJSLiftParameter() + let ret = arrayMembersFirst(_: _tmp_value, _: _tmp_values) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Greeter_init") @_cdecl("bjs_Greeter_init") public func _bjs_Greeter_init(_ nameBytes: Int32, _ nameLength: Int32) -> UnsafeMutableRawPointer { @@ -8591,6 +8706,95 @@ func _$_jsWeirdFunction() throws(JSException) -> Double { return Double.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripNumberArray") +fileprivate func bjs_jsRoundTripNumberArray() -> Void +#else +fileprivate func bjs_jsRoundTripNumberArray() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripNumberArray(_ values: [Double]) throws(JSException) -> [Double] { + let _ = values.bridgeJSLowerParameter() + bjs_jsRoundTripNumberArray() + if let error = _swift_js_take_exception() { + throw error + } + return [Double].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripStringArray") +fileprivate func bjs_jsRoundTripStringArray() -> Void +#else +fileprivate func bjs_jsRoundTripStringArray() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripStringArray(_ values: [String]) throws(JSException) -> [String] { + let _ = values.bridgeJSLowerParameter() + bjs_jsRoundTripStringArray() + if let error = _swift_js_take_exception() { + throw error + } + return [String].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripBoolArray") +fileprivate func bjs_jsRoundTripBoolArray() -> Void +#else +fileprivate func bjs_jsRoundTripBoolArray() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripBoolArray(_ values: [Bool]) throws(JSException) -> [Bool] { + let _ = values.bridgeJSLowerParameter() + bjs_jsRoundTripBoolArray() + if let error = _swift_js_take_exception() { + throw error + } + return [Bool].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsSumNumberArray") +fileprivate func bjs_jsSumNumberArray() -> Float64 +#else +fileprivate func bjs_jsSumNumberArray() -> Float64 { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double { + let _ = values.bridgeJSLowerParameter() + let ret = bjs_jsSumNumberArray() + if let error = _swift_js_take_exception() { + throw error + } + return Double.bridgeJSLiftReturn(ret) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsCreateNumberArray") +fileprivate func bjs_jsCreateNumberArray() -> Void +#else +fileprivate func bjs_jsCreateNumberArray() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsCreateNumberArray() throws(JSException) -> [Double] { + bjs_jsCreateNumberArray() + if let error = _swift_js_take_exception() { + throw error + } + return [Double].bridgeJSLiftReturn() +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_parseInt") fileprivate func bjs_parseInt(_ string: Int32) -> Float64 @@ -9022,6 +9226,209 @@ func _$Animal_getIsCat(_ self: JSObject) throws(JSException) -> Bool { return Bool.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripIntArray") +fileprivate func bjs_jsRoundTripIntArray() -> Void +#else +fileprivate func bjs_jsRoundTripIntArray() -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripIntArray(_ items: [Int]) throws(JSException) -> [Int] { + let _ = items.bridgeJSLowerParameter() + bjs_jsRoundTripIntArray() + if let error = _swift_js_take_exception() { + throw error + } + return [Int].bridgeJSLiftReturn() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsArrayLength") +fileprivate func bjs_jsArrayLength() -> Int32 +#else +fileprivate func bjs_jsArrayLength() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsArrayLength(_ items: [Int]) throws(JSException) -> Int { + let _ = items.bridgeJSLowerParameter() + let ret = bjs_jsArrayLength() + if let error = _swift_js_take_exception() { + throw error + } + return Int.bridgeJSLiftReturn(ret) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_makeArrayHost") +fileprivate func bjs_makeArrayHost() -> Int32 +#else +fileprivate func bjs_makeArrayHost() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +func _$makeArrayHost(_ numbers: [Int], _ labels: [String]) throws(JSException) -> ArrayHost { + let _ = labels.bridgeJSLowerParameter() + let _ = numbers.bridgeJSLowerParameter() + let ret = bjs_makeArrayHost() + if let error = _swift_js_take_exception() { + throw error + } + return ArrayHost.bridgeJSLiftReturn(ret) +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_init") +fileprivate func bjs_ArrayHost_init() -> Int32 +#else +fileprivate func bjs_ArrayHost_init() -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_numbers_get") +fileprivate func bjs_ArrayHost_numbers_get(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_numbers_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_labels_get") +fileprivate func bjs_ArrayHost_labels_get(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_labels_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_numbers_set") +fileprivate func bjs_ArrayHost_numbers_set(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_numbers_set(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_labels_set") +fileprivate func bjs_ArrayHost_labels_set(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_labels_set(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_concatNumbers") +fileprivate func bjs_ArrayHost_concatNumbers(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_concatNumbers(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_concatLabels") +fileprivate func bjs_ArrayHost_concatLabels(_ self: Int32) -> Void +#else +fileprivate func bjs_ArrayHost_concatLabels(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ArrayHost_firstLabel") +fileprivate func bjs_ArrayHost_firstLabel(_ self: Int32) -> Int32 +#else +fileprivate func bjs_ArrayHost_firstLabel(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +func _$ArrayHost_init(_ numbers: [Int], _ labels: [String]) throws(JSException) -> JSObject { + let _ = labels.bridgeJSLowerParameter() + let _ = numbers.bridgeJSLowerParameter() + let ret = bjs_ArrayHost_init() + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) +} + +func _$ArrayHost_numbers_get(_ self: JSObject) throws(JSException) -> [Int] { + let selfValue = self.bridgeJSLowerParameter() + bjs_ArrayHost_numbers_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return [Int].bridgeJSLiftReturn() +} + +func _$ArrayHost_labels_get(_ self: JSObject) throws(JSException) -> [String] { + let selfValue = self.bridgeJSLowerParameter() + bjs_ArrayHost_labels_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return [String].bridgeJSLiftReturn() +} + +func _$ArrayHost_numbers_set(_ self: JSObject, _ newValue: [Int]) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let _ = newValue.bridgeJSLowerParameter() + bjs_ArrayHost_numbers_set(selfValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$ArrayHost_labels_set(_ self: JSObject, _ newValue: [String]) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let _ = newValue.bridgeJSLowerParameter() + bjs_ArrayHost_labels_set(selfValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$ArrayHost_concatNumbers(_ self: JSObject, _ values: [Int]) throws(JSException) -> [Int] { + let selfValue = self.bridgeJSLowerParameter() + let _ = values.bridgeJSLowerParameter() + bjs_ArrayHost_concatNumbers(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return [Int].bridgeJSLiftReturn() +} + +func _$ArrayHost_concatLabels(_ self: JSObject, _ values: [String]) throws(JSException) -> [String] { + let selfValue = self.bridgeJSLowerParameter() + let _ = values.bridgeJSLowerParameter() + bjs_ArrayHost_concatLabels(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return [String].bridgeJSLiftReturn() +} + +func _$ArrayHost_firstLabel(_ self: JSObject, _ values: [String]) throws(JSException) -> String { + let selfValue = self.bridgeJSLowerParameter() + let _ = values.bridgeJSLowerParameter() + let ret = bjs_ArrayHost_firstLabel(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return String.bridgeJSLiftReturn(ret) +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsApplyInt") fileprivate func bjs_jsApplyInt(_ value: Int32, _ transform: UnsafeMutableRawPointer) -> Int32 diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 41d2ccf89..366115792 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -10917,6 +10917,112 @@ "_0" : "FooContainer" } } + }, + { + "abiName" : "bjs_roundTripArrayMembers", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripArrayMembers", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "swiftStruct" : { + "_0" : "ArrayMembers" + } + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "ArrayMembers" + } + } + }, + { + "abiName" : "bjs_arrayMembersSum", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "arrayMembersSum", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "swiftStruct" : { + "_0" : "ArrayMembers" + } + } + }, + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_arrayMembersFirst", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "arrayMembersFirst", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "swiftStruct" : { + "_0" : "ArrayMembers" + } + } + }, + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } } ], "protocols" : [ @@ -12547,6 +12653,110 @@ } ], "swiftCallName" : "FooContainer" + }, + { + "methods" : [ + { + "abiName" : "bjs_ArrayMembers_sumValues", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "sumValues", + "parameters" : [ + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_ArrayMembers_firstString", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "firstString", + "parameters" : [ + { + "label" : "_", + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + } + ], + "name" : "ArrayMembers", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "ints", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "optStrings", + "type" : { + "nullable" : { + "_0" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + }, + "_1" : "null" + } + } + } + ], + "swiftCallName" : "ArrayMembers" } ] }, @@ -12850,70 +13060,185 @@ } }, { - "from" : "global", - "name" : "parseInt", + "name" : "jsRoundTripNumberArray", "parameters" : [ { - "name" : "string", + "name" : "values", "type" : { - "string" : { + "array" : { + "_0" : { + "double" : { + } + } } } } ], "returnType" : { - "double" : { - - } - } - } - ], - "globalGetters" : [ - { - "from" : "global", - "name" : "globalObject1", - "type" : { - "jsValue" : { + "array" : { + "_0" : { + "double" : { + } + } } } - } - ], - "types" : [ + }, { - "constructor" : { - "parameters" : [ - { - "name" : "name", - "type" : { - "string" : { - - } - } - }, - { - "name" : "prefix", - "type" : { - "string" : { + "name" : "jsRoundTripStringArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + } } } } - ] - }, - "getters" : [ - { - "name" : "name", - "type" : { + } + ], + "returnType" : { + "array" : { + "_0" : { "string" : { } } - }, - { - "name" : "prefix", - "type" : { + } + } + }, + { + "name" : "jsRoundTripBoolArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "name" : "jsSumNumberArray", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "name" : "jsCreateNumberArray", + "parameters" : [ + + ], + "returnType" : { + "array" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "from" : "global", + "name" : "parseInt", + "parameters" : [ + { + "name" : "string", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + } + ], + "globalGetters" : [ + { + "from" : "global", + "name" : "globalObject1", + "type" : { + "jsValue" : { + + } + } + } + ], + "types" : [ + { + "constructor" : { + "parameters" : [ + { + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "name" : "prefix", + "type" : { + "string" : { + + } + } + } + ] + }, + "getters" : [ + { + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "name" : "prefix", + "type" : { "string" : { } @@ -13203,6 +13528,258 @@ } ] }, + { + "functions" : [ + { + "name" : "jsRoundTripIntArray", + "parameters" : [ + { + "name" : "items", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "jsArrayLength", + "parameters" : [ + { + "name" : "items", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "name" : "makeArrayHost", + "parameters" : [ + { + "name" : "numbers", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "labels", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "jsObject" : { + "_0" : "ArrayHost" + } + } + } + ], + "types" : [ + { + "constructor" : { + "parameters" : [ + { + "name" : "numbers", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "labels", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ] + }, + "getters" : [ + { + "name" : "numbers", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "labels", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "methods" : [ + { + "name" : "concatNumbers", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "name" : "concatLabels", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "name" : "firstLabel", + "parameters" : [ + { + "name" : "values", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "ArrayHost", + "setters" : [ + { + "functionName" : "numbers_set", + "name" : "numbers", + "type" : { + "array" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "functionName" : "labels_set", + "name" : "labels", + "type" : { + "array" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "staticMethods" : [ + + ] + } + ] + }, { "functions" : [ { diff --git a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift index 460297133..a465c57b4 100644 --- a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift @@ -152,6 +152,33 @@ class ImportAPITests: XCTestCase { XCTAssertEqual(prefixer("world!"), "Hello, world!") } + func testRoundTripIntArray() throws { + let values = [1, 2, 3, 4, 5] + let result = try jsRoundTripIntArray(values) + XCTAssertEqual(result, values) + XCTAssertEqual(try jsArrayLength(values), values.count) + XCTAssertEqual(try jsRoundTripIntArray([]), []) + } + + func testJSClassArrayMembers() throws { + let numbers = [1, 2, 3] + let labels = ["alpha", "beta"] + let host = try makeArrayHost(numbers, labels) + + XCTAssertEqual(try host.numbers, numbers) + XCTAssertEqual(try host.labels, labels) + + try host.setNumbers([10, 20]) + try host.setLabels(["gamma"]) + XCTAssertEqual(try host.numbers, [10, 20]) + XCTAssertEqual(try host.labels, ["gamma"]) + + XCTAssertEqual(try host.concatNumbers([30, 40]), [10, 20, 30, 40]) + XCTAssertEqual(try host.concatLabels(["delta", "epsilon"]), ["gamma", "delta", "epsilon"]) + XCTAssertEqual(try host.firstLabel([]), "gamma") + XCTAssertEqual(try host.firstLabel(["zeta"]), "zeta") + } + func testClosureParameterIntToVoid() throws { var total = 0 let ret = try jsCallTwice(5) { total += $0 } @@ -178,4 +205,38 @@ class ImportAPITests: XCTestCase { let dashed = try StaticBox.with_dashes() XCTAssertEqual(try dashed.value(), 7) } + + func testRoundTripNumberArray() throws { + let input: [Double] = [1.0, 2.5, 3.0, -4.5] + let result = try jsRoundTripNumberArray(input) + XCTAssertEqual(result, input) + XCTAssertEqual(try jsRoundTripNumberArray([]), []) + XCTAssertEqual(try jsRoundTripNumberArray([42.0]), [42.0]) + } + + func testRoundTripStringArray() throws { + let input = ["Hello", "World", "🎉"] + let result = try jsRoundTripStringArray(input) + XCTAssertEqual(result, input) + XCTAssertEqual(try jsRoundTripStringArray([]), []) + XCTAssertEqual(try jsRoundTripStringArray(["", "a", ""]), ["", "a", ""]) + } + + func testRoundTripBoolArray() throws { + let input = [true, false, true, false] + let result = try jsRoundTripBoolArray(input) + XCTAssertEqual(result, input) + XCTAssertEqual(try jsRoundTripBoolArray([]), []) + } + + func testSumNumberArray() throws { + XCTAssertEqual(try jsSumNumberArray([1.0, 2.0, 3.0, 4.0]), 10.0) + XCTAssertEqual(try jsSumNumberArray([]), 0.0) + XCTAssertEqual(try jsSumNumberArray([42.0]), 42.0) + } + + func testCreateNumberArray() throws { + let result = try jsCreateNumberArray() + XCTAssertEqual(result, [1.0, 2.0, 3.0, 4.0, 5.0]) + } } diff --git a/Tests/BridgeJSRuntimeTests/ImportArrayAPIs.swift b/Tests/BridgeJSRuntimeTests/ImportArrayAPIs.swift new file mode 100644 index 000000000..3f33de86a --- /dev/null +++ b/Tests/BridgeJSRuntimeTests/ImportArrayAPIs.swift @@ -0,0 +1,17 @@ +@_spi(Experimental) @_spi(BridgeJS) import JavaScriptKit + +@JSFunction func jsRoundTripIntArray(_ items: [Int]) throws(JSException) -> [Int] +@JSFunction func jsArrayLength(_ items: [Int]) throws(JSException) -> Int + +@JSClass struct ArrayHost { + @JSGetter var numbers: [Int] + @JSGetter var labels: [String] + @JSSetter func setNumbers(_ value: [Int]) throws(JSException) + @JSSetter func setLabels(_ value: [String]) throws(JSException) + @JSFunction init(_ numbers: [Int], _ labels: [String]) throws(JSException) + @JSFunction func concatNumbers(_ values: [Int]) throws(JSException) -> [Int] + @JSFunction func concatLabels(_ values: [String]) throws(JSException) -> [String] + @JSFunction func firstLabel(_ values: [String]) throws(JSException) -> String +} + +@JSFunction func makeArrayHost(_ numbers: [Int], _ labels: [String]) throws(JSException) -> ArrayHost diff --git a/Tests/BridgeJSRuntimeTests/StructAPIs.swift b/Tests/BridgeJSRuntimeTests/StructAPIs.swift index c7de5d137..49869e0f5 100644 --- a/Tests/BridgeJSRuntimeTests/StructAPIs.swift +++ b/Tests/BridgeJSRuntimeTests/StructAPIs.swift @@ -234,3 +234,28 @@ @JS func roundTripFooContainer(_ container: FooContainer) -> FooContainer { return container } + +@JS struct ArrayMembers { + var ints: [Int] + var optStrings: [String]? + + @JS func sumValues(_ values: [Int]) -> Int { + values.reduce(0, +) + } + + @JS func firstString(_ values: [String]) -> String? { + values.first + } +} + +@JS func roundTripArrayMembers(_ value: ArrayMembers) -> ArrayMembers { + value +} + +@JS func arrayMembersSum(_ value: ArrayMembers, _ values: [Int]) -> Int { + value.sumValues(values) +} + +@JS func arrayMembersFirst(_ value: ArrayMembers, _ values: [String]) -> String? { + value.firstString(values) +} diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 03ecb5b39..b7dc6c48d 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -44,3 +44,9 @@ export class StaticBox { static makeDefault(): StaticBox; static "with-dashes"(): StaticBox; } + +export function jsRoundTripNumberArray(values: number[]): number[]; +export function jsRoundTripStringArray(values: string[]): string[]; +export function jsRoundTripBoolArray(values: boolean[]): boolean[]; +export function jsSumNumberArray(values: number[]): number; +export function jsCreateNumberArray(): number[]; diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index cc21a864a..e8f0b081c 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -33,6 +33,22 @@ export async function setupOptions(options, context) { return { ...options, getImports: (importsContext) => { + const ArrayHost = class { + constructor(numbers, labels) { + this.numbers = numbers; + this.labels = labels; + } + concatNumbers(values) { + return [...this.numbers, ...values]; + } + concatLabels(values) { + return [...this.labels, ...values]; + } + firstLabel(values) { + const merged = [...values, ...this.labels]; + return merged.length > 0 ? merged[0] : ""; + } + }; return { "jsRoundTripVoid": () => { return; @@ -55,6 +71,12 @@ export async function setupOptions(options, context) { "jsRoundTripJSValue": (v) => { return v; }, + "jsRoundTripIntArray": (items) => { + return items; + }, + "jsArrayLength": (items) => { + return items.length; + }, "jsThrowOrVoid": (shouldThrow) => { if (shouldThrow) { throw new Error("TestError"); @@ -87,6 +109,10 @@ export async function setupOptions(options, context) { "$jsWeirdFunction": () => { return 42; }, + ArrayHost, + makeArrayHost: (numbers, labels) => { + return new ArrayHost(numbers, labels); + }, JsGreeter: class { /** * @param {string} name @@ -140,6 +166,24 @@ export async function setupOptions(options, context) { }, jsTranslatePoint: (point, dx, dy) => { return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) }; + }, + jsRoundTripNumberArray: (values) => { + return values; + }, + jsRoundTripStringArray: (values) => { + return values; + }, + jsRoundTripBoolArray: (values) => { + return values; + }, + jsSumNumberArray: (values) => { + return values.reduce((a, b) => a + b, 0); + }, + jsCreateNumberArray: () => { + return [1, 2, 3, 4, 5]; + }, + roundTripArrayMembers: (value) => { + return value; } }; }, @@ -223,6 +267,12 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { ]) { assert.equal(exports.roundTripString(v), v); } + const arrayStruct = { ints: [1, 2, 3], optStrings: ["a", "b"] }; + const arrayStructRoundTrip = exports.roundTripArrayMembers(arrayStruct); + assert.deepEqual(arrayStructRoundTrip.ints, [1, 2, 3]); + assert.deepEqual(arrayStructRoundTrip.optStrings, ["a", "b"]); + assert.equal(exports.arrayMembersSum(arrayStruct, [10, 20]), 30); + assert.equal(exports.arrayMembersFirst(arrayStruct, ["x", "y"]), "x"); for (const p of [1, 4, 1024, 65536, 2147483647]) { assert.equal(exports.roundTripUnsafeRawPointer(p), p);