From 516e46b95b8806ddb44b22019058ace0e2b4a7b3 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Fri, 22 Aug 2025 15:46:53 +0200 Subject: [PATCH 1/9] BridgeJS: Support for multiple associated values in enums using binary buffer format --- .../Sources/BridgeJSCore/ExportSwift.swift | 237 ++- .../Sources/BridgeJSLink/BridgeJSLink.swift | 395 +++- .../Sources/BridgeJSLink/JSGlueGen.swift | 57 +- .../Inputs/EnumAssociatedValue.swift | 40 + .../ArrayParameter.Import.js | 38 +- .../BridgeJSLinkTests/Async.Export.js | 38 +- .../BridgeJSLinkTests/Async.Import.js | 38 +- .../EnumAssociatedValue.Export.d.ts | 75 + .../EnumAssociatedValue.Export.js | 445 ++++ .../BridgeJSLinkTests/EnumCase.Export.js | 38 +- .../BridgeJSLinkTests/EnumNamespace.Export.js | 38 +- .../BridgeJSLinkTests/EnumRawType.Export.js | 38 +- .../BridgeJSLinkTests/Interface.Import.js | 38 +- .../InvalidPropertyNames.Import.js | 38 +- .../MultipleImportedTypes.Import.js | 38 +- .../BridgeJSLinkTests/Namespaces.Export.js | 38 +- .../PrimitiveParameters.Export.js | 38 +- .../PrimitiveParameters.Import.js | 38 +- .../PrimitiveReturn.Export.js | 38 +- .../PrimitiveReturn.Import.js | 38 +- .../BridgeJSLinkTests/PropertyTypes.Export.js | 38 +- .../StringParameter.Export.js | 38 +- .../StringParameter.Import.js | 38 +- .../BridgeJSLinkTests/StringReturn.Export.js | 38 +- .../BridgeJSLinkTests/StringReturn.Import.js | 38 +- .../BridgeJSLinkTests/SwiftClass.Export.js | 38 +- .../TS2SkeletonLike.Import.js | 38 +- .../BridgeJSLinkTests/Throws.Export.js | 38 +- .../BridgeJSLinkTests/TypeAlias.Import.js | 38 +- .../TypeScriptClass.Import.js | 38 +- .../VoidParameterVoidReturn.Export.js | 38 +- .../VoidParameterVoidReturn.Import.js | 38 +- .../ExportSwiftTests/EnumAssociatedValue.json | 435 ++++ .../EnumAssociatedValue.swift | 348 ++++ .../ExportSwiftTests/EnumNamespace.swift | 3 - .../JavaScriptKit/BridgeJSInstrincics.swift | 161 ++ .../Exporting-Swift/Exporting-Swift-Enum.md | 136 +- .../BridgeJSRuntimeTests/ExportAPITests.swift | 137 ++ .../Generated/BridgeJS.ExportSwift.swift | 572 ++++- .../JavaScript/BridgeJS.ExportSwift.json | 1834 +++++++++++++---- Tests/prelude.mjs | 89 +- 41 files changed, 5415 insertions(+), 537 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 117158df..af23c4d0 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -25,6 +25,7 @@ public class ExportSwift { private var exportedClasses: [ExportedClass] = [] private var exportedEnums: [ExportedEnum] = [] private var typeDeclResolver: TypeDeclResolver = TypeDeclResolver() + private let enumCodegen: EnumCodegen = EnumCodegen() public init(progress: ProgressReporting, moduleName: String) { self.progress = progress @@ -524,6 +525,31 @@ public class ExportSwift { ) } + if currentEnum.cases.contains(where: { !$0.associatedValues.isEmpty }) { + if case .tsEnum = emitStyle { + diagnose( + node: jsAttribute, + message: "TypeScript enum style is not supported for associated value enums", + hint: "Use enumStyle: .const in order to map associated-value enums" + ) + } + for enumCase in currentEnum.cases { + for associatedValue in enumCase.associatedValues { + switch associatedValue.type { + case .string, .int, .float, .double, .bool: + break + default: + diagnose( + node: node, + message: "Unsupported associated value type: \(associatedValue.type.swiftType)", + hint: + "Only primitive types (String, Int, Float, Double, Bool) are supported in associated-value enums" + ) + } + } + } + } + let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: enumName) let explicitAccessControl = computeExplicitAtLeastInternalAccessControl( for: node, @@ -753,10 +779,15 @@ public class ExportSwift { decls.append(Self.prelude) for enumDef in exportedEnums { - if enumDef.enumType == .simple { - decls.append(renderCaseEnumHelpers(enumDef)) - } else { + switch enumDef.enumType { + case .simple: + decls.append(enumCodegen.renderCaseEnumHelpers(enumDef)) + case .rawValue: decls.append("extension \(raw: enumDef.swiftCallName): _BridgedSwiftEnumNoPayload {}") + case .associatedValue: + decls.append(enumCodegen.renderAssociatedValueEnumHelpers(enumDef)) + case .namespace: + () } } @@ -770,45 +801,6 @@ public class ExportSwift { return decls.map { $0.formatted(using: format).description }.joined(separator: "\n\n") } - func renderCaseEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax { - let typeName = enumDef.swiftCallName - var initCases: [String] = [] - var valueCases: [String] = [] - for (index, c) in enumDef.cases.enumerated() { - initCases.append("case \(index): self = .\(c.name)") - valueCases.append("case .\(c.name): return \(index)") - } - let initSwitch = (["switch bridgeJSRawValue {"] + initCases + ["default: return nil", "}"]).joined( - separator: "\n" - ) - let valueSwitch = (["switch self {"] + valueCases + ["}"]).joined(separator: "\n") - - return """ - extension \(raw: typeName) { - @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { - return bridgeJSRawValue - } - @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> \(raw: typeName) { - return \(raw: typeName)(bridgeJSRawValue: value)! - } - @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> \(raw: typeName) { - return \(raw: typeName)(bridgeJSRawValue: value)! - } - @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { - return bridgeJSRawValue - } - - private init?(bridgeJSRawValue: Int32) { - \(raw: initSwitch) - } - - private var bridgeJSRawValue: Int32 { - \(raw: valueSwitch) - } - } - """ - } - class ExportedThunkBuilder { var body: [CodeBlockItemSyntax] = [] var liftedParameterExprs: [ExprSyntax] = [] @@ -1006,6 +998,159 @@ public class ExportSwift { } } + private struct EnumCodegen { + func renderCaseEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax { + let typeName = enumDef.swiftCallName + var initCases: [String] = [] + var valueCases: [String] = [] + for (index, enumCase) in enumDef.cases.enumerated() { + initCases.append("case \(index): self = .\(enumCase.name)") + valueCases.append("case .\(enumCase.name): return \(index)") + } + let initSwitch = (["switch bridgeJSRawValue {"] + initCases + ["default: return nil", "}"]).joined( + separator: "\n" + ) + let valueSwitch = (["switch self {"] + valueCases + ["}"]).joined(separator: "\n") + + return """ + extension \(raw: typeName) { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> \(raw: typeName) { + return \(raw: typeName)(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> \(raw: typeName) { + return \(raw: typeName)(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + \(raw: initSwitch) + } + + private var bridgeJSRawValue: Int32 { + \(raw: valueSwitch) + } + } + """ + } + + func renderAssociatedValueEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax { + let typeName = enumDef.swiftCallName + return """ + private extension \(raw: typeName) { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> \(raw: typeName) { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + \(raw: generateBinaryLiftSwitchCases(enumDef: enumDef).joined(separator: "\n")) + default: fatalError("Unknown \(raw: typeName) case ID: \\(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + \(raw: generateReturnSwitchCases(enumDef: enumDef).joined(separator: "\n")) + } + } + } + """ + } + + private func generateBinaryLiftSwitchCases(enumDef: ExportedEnum) -> [String] { + var cases: [String] = [] + for (caseIndex, enumCase) in enumDef.cases.enumerated() { + if enumCase.associatedValues.isEmpty { + cases.append("case \(caseIndex): return .\(enumCase.name)") + } else { + var lines: [String] = [] + lines.append("case \(caseIndex):") + lines.append("reader.readParamCount(expected: \(enumCase.associatedValues.count))") + var argList: [String] = [] + + for (paramIndex, associatedValue) in enumCase.associatedValues.enumerated() { + let paramName = associatedValue.label ?? "param\(paramIndex)" + argList.append(paramName) + + switch associatedValue.type { + case .string: + lines.append("reader.expectTag(.string)") + lines.append("let \(paramName) = reader.readString()") + case .int: + lines.append("reader.expectTag(.int32)") + lines.append("let \(paramName) = Int(reader.readInt32())") + case .bool: + lines.append("reader.expectTag(.bool)") + lines.append("let \(paramName) = Int32(reader.readUInt8()) != 0") + case .float: + lines.append("reader.expectTag(.float32)") + lines.append("let \(paramName) = reader.readFloat32()") + case .double: + lines.append("reader.expectTag(.float64)") + lines.append("let \(paramName) = reader.readFloat64()") + default: + lines.append("reader.expectTag(.int32)") + lines.append("let \(paramName) = reader.readInt32()") + } + } + + lines.append("return .\(enumCase.name)(\(argList.joined(separator: ", ")))") + cases.append(lines.joined(separator: "\n")) + } + } + return cases + } + + private func generateReturnSwitchCases(enumDef: ExportedEnum) -> [String] { + var cases: [String] = [] + for (caseIndex, enumCase) in enumDef.cases.enumerated() { + if enumCase.associatedValues.isEmpty { + cases.append("case .\(enumCase.name):") + cases.append("_swift_js_return_tag(Int32(\(caseIndex)))") + } else { + var bodyLines: [String] = [] + bodyLines.append("_swift_js_return_tag(Int32(\(caseIndex)))") + for (index, associatedValue) in enumCase.associatedValues.enumerated() { + let paramName = associatedValue.label ?? "param\(index)" + switch associatedValue.type { + case .string: + bodyLines.append("var __bjs_\(paramName) = \(paramName)") + bodyLines.append("__bjs_\(paramName).withUTF8 { ptr in") + bodyLines.append("_swift_js_return_string(ptr.baseAddress, Int32(ptr.count))") + bodyLines.append("}") + case .int: + bodyLines.append("_swift_js_return_int(Int32(\(paramName)))") + case .bool: + bodyLines.append("_swift_js_return_bool(\(paramName) ? 1 : 0)") + case .float: + bodyLines.append("_swift_js_return_f32(\(paramName))") + case .double: + bodyLines.append("_swift_js_return_f64(\(paramName))") + default: + bodyLines.append( + "preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")" + ) + } + } + let pattern = enumCase.associatedValues.enumerated() + .map { index, associatedValue in "let \(associatedValue.label ?? "param\(index)")" } + .joined(separator: ", ") + cases.append("case .\(enumCase.name)(\(pattern)):") + cases.append(contentsOf: bodyLines) + } + } + return cases + } + } + func renderSingleExportedFunction(function: ExportedFunction) throws -> DeclSyntax { let builder = ExportedThunkBuilder(effects: function.effects) for param in function.parameters { @@ -1264,6 +1409,9 @@ extension BridgeType { static let swiftHeapObject = LiftingIntrinsicInfo(parameters: [("value", .pointer)]) static let void = LiftingIntrinsicInfo(parameters: []) static let caseEnum = LiftingIntrinsicInfo(parameters: [("value", .i32)]) + static let associatedValueEnum = LiftingIntrinsicInfo(parameters: [ + ("caseId", .i32), ("paramsId", .i32), ("paramsLen", .i32), + ]) } func liftParameterInfo() throws -> LiftingIntrinsicInfo { @@ -1291,7 +1439,7 @@ extension BridgeType { case .uint64: return .int } case .associatedValueEnum: - throw BridgeJSCoreError("Associated value enums are not supported to pass as parameters") + return .associatedValueEnum case .namespaceEnum: throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") } @@ -1310,6 +1458,7 @@ extension BridgeType { static let void = LoweringIntrinsicInfo(returnType: nil) static let caseEnum = LoweringIntrinsicInfo(returnType: .i32) static let rawValueEnum = LoweringIntrinsicInfo(returnType: .i32) + static let associatedValueEnum = LoweringIntrinsicInfo(returnType: nil) } func loweringReturnInfo() throws -> LoweringIntrinsicInfo { @@ -1337,7 +1486,7 @@ extension BridgeType { case .uint64: return .int } case .associatedValueEnum: - throw BridgeJSCoreError("Associated value enums are not supported to pass as parameters") + return .associatedValueEnum case .namespaceEnum: throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 360a6274..75c07466 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -65,6 +65,57 @@ struct BridgeJSLink { } """ + let sharedEnumHelpersJs = """ + /// Shared helpers for encoding associated-value enums between JS and Swift. + const __bjs_ParamType = { STRING: 1, INT32: 2, BOOL: 3, FLOAT32: 4, FLOAT64: 5 }; + function __bjs_encodeEnumParams(textEncoder, swift, parts) { + const SIZE_U8 = 1, SIZE_U32 = 4, SIZE_F32 = 4, SIZE_F64 = 8; + let totalLen = SIZE_U32; + for (const p of parts) { + switch (p.t) { + case __bjs_ParamType.STRING: { + const bytes = textEncoder.encode(p.v); + p._bytes = bytes; + totalLen += SIZE_U8 + SIZE_U32 + bytes.length; + break; + } + case __bjs_ParamType.INT32: totalLen += SIZE_U8 + SIZE_U32; break; + case __bjs_ParamType.BOOL: totalLen += SIZE_U8 + SIZE_U8; break; + case __bjs_ParamType.FLOAT32: totalLen += SIZE_U8 + SIZE_F32; break; + case __bjs_ParamType.FLOAT64: totalLen += SIZE_U8 + SIZE_F64; break; + default: throw new Error("Unsupported param type tag: " + p.t); + } + } + const buf = new Uint8Array(totalLen); + const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + let off = 0; + view.setUint32(off, parts.length, true); off += SIZE_U32; + for (const p of parts) { + view.setUint8(off, p.t); off += SIZE_U8; + switch (p.t) { + case __bjs_ParamType.STRING: { + const b = p._bytes; + view.setUint32(off, b.length, true); off += SIZE_U32; + buf.set(b, off); off += b.length; + break; + } + case __bjs_ParamType.INT32: + view.setInt32(off, (p.v | 0), true); off += SIZE_U32; break; + case __bjs_ParamType.BOOL: + view.setUint8(off, p.v ? 1 : 0); off += SIZE_U8; break; + case __bjs_ParamType.FLOAT32: + view.setFloat32(off, Math.fround(p.v), true); off += SIZE_F32; break; + case __bjs_ParamType.FLOAT64: + view.setFloat64(off, p.v, true); off += SIZE_F64; break; + default: throw new Error("Unsupported param type tag: " + p.t); + } + } + const paramsId = swift.memory.retain(buf); + return { paramsId, paramsLen: buf.length, cleanup: () => { swift.memory.release(paramsId); } }; + } + + """ + func link() throws -> (outputJs: String, outputDts: String) { var exportsLines: [String] = [] var classLines: [String] = [] @@ -73,8 +124,6 @@ struct BridgeJSLink { var namespacedFunctions: [ExportedFunction] = [] var namespacedClasses: [ExportedClass] = [] var namespacedEnums: [ExportedEnum] = [] - var enumConstantLines: [String] = [] - var dtsEnumLines: [String] = [] var topLevelEnumLines: [String] = [] var topLevelDtsEnumLines: [String] = [] @@ -123,12 +172,15 @@ struct BridgeJSLink { namespacedEnums.append(enumDefinition) } case .associatedValue: - enumConstantLines.append(contentsOf: jsEnum) - exportsLines.append("\(enumDefinition.name),") + var exportedJsEnum = jsEnum + if !exportedJsEnum.isEmpty && exportedJsEnum[0].hasPrefix("const ") { + exportedJsEnum[0] = "export " + exportedJsEnum[0] + } + topLevelEnumLines.append(contentsOf: exportedJsEnum) + topLevelDtsEnumLines.append(contentsOf: dtsEnum) if enumDefinition.namespace != nil { namespacedEnums.append(enumDefinition) } - dtsEnumLines.append(contentsOf: dtsEnum) } } } @@ -161,7 +213,7 @@ struct BridgeJSLink { importObjectBuilders.append(importObjectBuilder) } - let hasNamespacedItems = !namespacedFunctions.isEmpty || !namespacedClasses.isEmpty || !namespacedEnums.isEmpty + let hasNamespacedItems = !namespacedFunctions.isEmpty || !namespacedClasses.isEmpty let namespaceBuilder = NamespaceBuilder() let namespaceDeclarationsLines = namespaceBuilder.namespaceDeclarations( @@ -173,21 +225,15 @@ struct BridgeJSLink { let exportsSection: String if hasNamespacedItems { - let namespacedEnumsForExports = namespacedEnums.filter { $0.enumType == .associatedValue } let namespaceSetupCode = namespaceBuilder.renderGlobalNamespace( namespacedFunctions: namespacedFunctions, - namespacedClasses: namespacedClasses, - namespacedEnums: namespacedEnumsForExports + namespacedClasses: namespacedClasses ) .map { $0.indent(count: 12) }.joined(separator: "\n") - let enumSection = - enumConstantLines.isEmpty - ? "" : enumConstantLines.map { $0.indent(count: 12) }.joined(separator: "\n") + "\n" - exportsSection = """ \(classLines.map { $0.indent(count: 12) }.joined(separator: "\n")) - \(enumSection)\("const exports = {".indent(count: 12)) + \("const exports = {".indent(count: 12)) \(exportsLines.map { $0.indent(count: 16) }.joined(separator: "\n")) \("};".indent(count: 12)) @@ -197,27 +243,31 @@ struct BridgeJSLink { }, """ } else { - let enumSection = - enumConstantLines.isEmpty - ? "" : enumConstantLines.map { $0.indent(count: 12) }.joined(separator: "\n") + "\n" - exportsSection = """ \(classLines.map { $0.indent(count: 12) }.joined(separator: "\n")) - \(enumSection)\("return {".indent(count: 12)) + \("return {".indent(count: 12)) \(exportsLines.map { $0.indent(count: 16) }.joined(separator: "\n")) \("};".indent(count: 12)) }, """ } + let hasAssociatedValueEnums = exportedSkeletons.contains { skeleton in + skeleton.enums.contains { $0.enumType == .associatedValue } + } + let sharedEnumHelpersSection = hasAssociatedValueEnums ? sharedEnumHelpersJs : "" let topLevelEnumsSection = topLevelEnumLines.isEmpty ? "" : topLevelEnumLines.joined(separator: "\n") + "\n\n" - let topLevelNamespaceCode = namespaceBuilder.renderTopLevelEnumNamespaceAssignments( namespacedEnums: namespacedEnums ) let namespaceAssignmentsSection = topLevelNamespaceCode.isEmpty ? "" : topLevelNamespaceCode.joined(separator: "\n") + "\n\n" + let enumHelpers = renderEnumHelperAssignments() + let enumHelpersSection = + enumHelpers.isEmpty ? "" : "\n\n" + enumHelpers.map { $0.indent(count: 12) }.joined(separator: "\n") + let enumHelpersDeclaration = hasAssociatedValueEnums ? "const enumHelpers = {};\n" : "\n" + let outputJs = """ // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. @@ -225,16 +275,22 @@ struct BridgeJSLink { // To update this file, just rebuild your project or run // `swift package bridge-js`. - \(topLevelEnumsSection)\(namespaceAssignmentsSection)export async function createInstantiator(options, \(JSGlueVariableScope.reservedSwift)) { + \(sharedEnumHelpersSection)\(topLevelEnumsSection)\(namespaceAssignmentsSection)export async function createInstantiator(options, \(JSGlueVariableScope.reservedSwift)) { let \(JSGlueVariableScope.reservedInstance); let \(JSGlueVariableScope.reservedMemory); let \(JSGlueVariableScope.reservedSetException); const \(JSGlueVariableScope.reservedTextDecoder) = new TextDecoder("utf-8"); const \(JSGlueVariableScope.reservedTextEncoder) = new TextEncoder("utf-8"); - let \(JSGlueVariableScope.reservedStorageToReturnString); let \(JSGlueVariableScope.reservedStorageToReturnBytes); let \(JSGlueVariableScope.reservedStorageToReturnException); + let \(JSGlueVariableScope.reservedTmpRetTag); + let \(JSGlueVariableScope.reservedTmpRetStrings) = []; + let \(JSGlueVariableScope.reservedTmpRetInts) = []; + let \(JSGlueVariableScope.reservedTmpRetF32s) = []; + let \(JSGlueVariableScope.reservedTmpRetF64s) = []; + let \(JSGlueVariableScope.reservedTmpRetBools) = []; + \(enumHelpersDeclaration) return { /** * @param {WebAssembly.Imports} importObject @@ -245,7 +301,9 @@ struct BridgeJSLink { const imports = options.getImports(importsContext); bjs["swift_js_return_string"] = function(ptr, len) { const bytes = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, ptr, len)\(sharedMemory ? ".slice()" : ""); - \(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes); + const value = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes); + \(JSGlueVariableScope.reservedStorageToReturnString) = value; + \(JSGlueVariableScope.reservedTmpRetStrings).push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = \(JSGlueVariableScope.reservedSwift).memory.getObject(sourceId); @@ -270,12 +328,37 @@ struct BridgeJSLink { bjs["swift_js_release"] = function(id) { \(JSGlueVariableScope.reservedSwift).memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + \(JSGlueVariableScope.reservedTmpRetTag) = tag | 0; + tmpRetString = undefined; + \(JSGlueVariableScope.reservedTmpRetStrings) = []; + \(JSGlueVariableScope.reservedTmpRetInts) = []; + \(JSGlueVariableScope.reservedTmpRetF32s) = []; + \(JSGlueVariableScope.reservedTmpRetF64s) = []; + \(JSGlueVariableScope.reservedTmpRetBools) = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + \(JSGlueVariableScope.reservedTmpRetInts).push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + \(JSGlueVariableScope.reservedTmpRetF32s).push(value); + } + bjs["swift_js_return_f64"] = function(v) { + \(JSGlueVariableScope.reservedTmpRetF64s).push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + \(JSGlueVariableScope.reservedTmpRetBools).push(value); + } \(renderSwiftClassWrappers().map { $0.indent(count: 12) }.joined(separator: "\n")) \(importObjectBuilders.flatMap { $0.importedLines }.map { $0.indent(count: 12) }.joined(separator: "\n")) }, setInstance: (i) => { \(JSGlueVariableScope.reservedInstance) = i; \(JSGlueVariableScope.reservedMemory) = \(JSGlueVariableScope.reservedInstance).exports.memory; + \(enumHelpersSection) \(JSGlueVariableScope.reservedSetException) = (error) => { \(JSGlueVariableScope.reservedInstance).exports._swift_js_exception.value = \(JSGlueVariableScope.reservedSwift).memory.retain(error) } @@ -291,7 +374,6 @@ struct BridgeJSLink { var dtsLines: [String] = [] dtsLines.append(contentsOf: namespaceDeclarationsLines) dtsLines.append(contentsOf: dtsClassLines) - dtsLines.append(contentsOf: dtsEnumLines) dtsLines.append(contentsOf: generateImportedTypeDefinitions()) dtsLines.append("export type Exports = {") dtsLines.append(contentsOf: dtsExportLines.map { $0.indent(count: 4) }) @@ -321,6 +403,21 @@ struct BridgeJSLink { return (outputJs, outputDts) } + private func renderEnumHelperAssignments() -> [String] { + var lines: [String] = [] + + for skeleton in exportedSkeletons { + for enumDef in skeleton.enums where enumDef.enumType == .associatedValue { + let base = enumDef.name + lines.append("const \(base)Helpers = __bjs_create\(base)Helpers()(textEncoder, swift);") + lines.append("enumHelpers.\(base) = \(base)Helpers;") + lines.append("") + } + } + + return lines + } + private func renderSwiftClassWrappers() -> [String] { var wrapperLines: [String] = [] var modulesByName: [String: [ExportedClass]] = [:] @@ -612,10 +709,185 @@ struct BridgeJSLink { dtsLines.append("") } } - case .associatedValue: - jsLines.append("// TODO: Implement \(enumDefinition.enumType) enum: \(enumDefinition.name)") - dtsLines.append("// TODO: Implement \(enumDefinition.enumType) enum: \(enumDefinition.name)") + do { + jsLines.append("const \(enumDefinition.name) = {") + jsLines.append("Tag: {".indent(count: 4)) + for (caseIndex, enumCase) in enumDefinition.cases.enumerated() { + let caseName = enumCase.name.capitalizedFirstLetter + jsLines.append("\(caseName): \(caseIndex),".indent(count: 8)) + } + jsLines.append("}".indent(count: 4)) + jsLines.append("};") + jsLines.append("") + jsLines.append("const __bjs_create\(enumDefinition.name)Helpers = () => {") + jsLines.append("return (textEncoder, swift) => ({".indent(count: 4)) + + jsLines.append("lower: (value) => {".indent(count: 8)) + jsLines.append("const enumTag = value.tag;".indent(count: 12)) + jsLines.append("switch (enumTag) {".indent(count: 12)) + for (_, enumCase) in enumDefinition.cases.enumerated() { + let caseName = enumCase.name.capitalizedFirstLetter + if enumCase.associatedValues.isEmpty { + jsLines.append("case \(enumDefinition.name).Tag.\(caseName): {".indent(count: 16)) + jsLines.append("const parts = [];".indent(count: 20)) + jsLines.append( + "const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts);" + .indent(count: 20) + ) + jsLines.append( + "return { caseId: \(enumDefinition.name).Tag.\(caseName), paramsId, paramsLen, cleanup };" + .indent(count: 20) + ) + jsLines.append("}".indent(count: 16)) + } else { + jsLines.append("case \(enumDefinition.name).Tag.\(caseName): {".indent(count: 16)) + var partLines: [String] = [] + for (associatedValueIndex, associatedValue) in enumCase.associatedValues.enumerated() { + let prop = associatedValue.label ?? "param\(associatedValueIndex)" + switch associatedValue.type { + case .string: + partLines.append("{ t: __bjs_ParamType.STRING, v: value.\(prop) }") + case .int: + partLines.append("{ t: __bjs_ParamType.INT32, v: (value.\(prop) | 0) }") + case .bool: + partLines.append("{ t: __bjs_ParamType.BOOL, v: value.\(prop) }") + case .float: + partLines.append("{ t: __bjs_ParamType.FLOAT32, v: value.\(prop) }") + case .double: + partLines.append("{ t: __bjs_ParamType.FLOAT64, v: value.\(prop) }") + default: + partLines.append("{ t: __bjs_ParamType.INT32, v: 0 }") + } + } + jsLines.append("const parts = [".indent(count: 20)) + if !partLines.isEmpty { + for (i, pl) in partLines.enumerated() { + let suffix = i == partLines.count - 1 ? "" : "," + jsLines.append("\(pl)\(suffix)".indent(count: 24)) + } + } + jsLines.append("];".indent(count: 20)) + jsLines.append( + "const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts);" + .indent(count: 20) + ) + jsLines.append( + "return { caseId: \(enumDefinition.name).Tag.\(caseName), paramsId, paramsLen, cleanup };" + .indent(count: 20) + ) + jsLines.append("}".indent(count: 16)) + } + } + jsLines.append( + "default: throw new Error(\"Unknown \(enumDefinition.name) tag: \" + String(enumTag));".indent( + count: 16 + ) + ) + jsLines.append("}".indent(count: 12)) + jsLines.append("},".indent(count: 8)) + + jsLines.append( + "raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools) => {".indent( + count: 8 + ) + ) + jsLines.append("const tag = tmpRetTag | 0;".indent(count: 12)) + jsLines.append("switch (tag) {".indent(count: 12)) + for (_, enumCase) in enumDefinition.cases.enumerated() { + let caseName = enumCase.name.capitalizedFirstLetter + if enumCase.associatedValues.isEmpty { + jsLines.append( + "case \(enumDefinition.name).Tag.\(caseName): return { tag: \(enumDefinition.name).Tag.\(caseName) };" + .indent(count: 16) + ) + } else { + var fields: [String] = ["tag: \(enumDefinition.name).Tag.\(caseName)"] + var stringIndex = 0 + var intIndex = 0 + var f32Index = 0 + var f64Index = 0 + var boolIndex = 0 + for (associatedValueIndex, associatedValue) in enumCase.associatedValues.enumerated() { + let prop = associatedValue.label ?? "param\(associatedValueIndex)" + switch associatedValue.type { + case .string: + fields.append("\(prop): tmpRetStrings[\(stringIndex)]") + stringIndex += 1 + case .bool: + fields.append("\(prop): tmpRetBools[\(boolIndex)]") + boolIndex += 1 + case .int: + fields.append("\(prop): tmpRetInts[\(intIndex)]") + intIndex += 1 + case .float: + fields.append("\(prop): tmpRetF32s[\(f32Index)]") + f32Index += 1 + case .double: + fields.append("\(prop): tmpRetF64s[\(f64Index)]") + f64Index += 1 + default: + fields.append("\(prop): undefined") + } + } + jsLines.append( + "case \(enumDefinition.name).Tag.\(caseName): return { \(fields.joined(separator: ", ")) };" + .indent(count: 16) + ) + } + } + jsLines.append( + "default: throw new Error(\"Unknown \(enumDefinition.name) tag returned from Swift: \" + String(tag));" + .indent( + count: 16 + ) + ) + jsLines.append("}".indent(count: 12)) + jsLines.append("}".indent(count: 8)) + jsLines.append("});".indent(count: 4)) + jsLines.append("};") + + if enumDefinition.namespace == nil { + dtsLines.append("export const \(enumDefinition.name): {") + dtsLines.append("readonly Tag: {".indent(count: 4)) + for (caseIndex, enumCase) in enumDefinition.cases.enumerated() { + let caseName = enumCase.name.capitalizedFirstLetter + dtsLines.append("readonly \(caseName): \(caseIndex);".indent(count: 8)) + } + dtsLines.append("};".indent(count: 4)) + dtsLines.append("};") + dtsLines.append("") + var unionParts: [String] = [] + for enumCase in enumDefinition.cases { + if enumCase.associatedValues.isEmpty { + unionParts.append( + "{ tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter) }" + ) + } else { + var fields: [String] = [ + "tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter)" + ] + for (associatedValueIndex, associatedValue) in enumCase.associatedValues + .enumerated() + { + let prop = associatedValue.label ?? "param\(associatedValueIndex)" + let ts: String + switch associatedValue.type { + case .string: ts = "string" + case .bool: ts = "boolean" + case .int, .float, .double: ts = "number" + default: ts = "never" + } + fields.append("\(prop): \(ts)") + } + unionParts.append("{ \(fields.joined(separator: "; ")) }") + } + } + dtsLines.append("export type \(enumDefinition.name) =") + dtsLines.append(" " + unionParts.joined(separator: " | ")) + dtsLines.append("") + } + } case .namespace: break } @@ -992,8 +1264,7 @@ struct BridgeJSLink { /// - Returns: Array of JavaScript code lines that set up the global namespace structure func renderGlobalNamespace( namespacedFunctions: [ExportedFunction], - namespacedClasses: [ExportedClass], - namespacedEnums: [ExportedEnum] + namespacedClasses: [ExportedClass] ) -> [String] { var lines: [String] = [] var uniqueNamespaces: [String] = [] @@ -1007,15 +1278,10 @@ struct BridgeJSLink { namespacedClasses .compactMap { $0.namespace } ) - let enumNamespacePaths: Set<[String]> = Set( - namespacedEnums - .compactMap { $0.namespace } - ) let allNamespacePaths = functionNamespacePaths .union(classNamespacePaths) - .union(enumNamespacePaths) allNamespacePaths.forEach { namespacePath in namespacePath.makeIterator().enumerated().forEach { (index, _) in @@ -1037,11 +1303,6 @@ struct BridgeJSLink { lines.append("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") } - namespacedEnums.forEach { enumDefinition in - let namespacePath: String = enumDefinition.namespace?.joined(separator: ".") ?? "" - lines.append("globalThis.\(namespacePath).\(enumDefinition.name) = exports.\(enumDefinition.name);") - } - namespacedFunctions.forEach { function in let namespacePath: String = function.namespace?.joined(separator: ".") ?? "" lines.append("globalThis.\(namespacePath).\(function.name) = exports.\(function.name);") @@ -1051,15 +1312,13 @@ struct BridgeJSLink { } func renderTopLevelEnumNamespaceAssignments(namespacedEnums: [ExportedEnum]) -> [String] { - let topLevelNamespacedEnums = namespacedEnums.filter { $0.enumType == .simple || $0.enumType == .rawValue } - - guard !topLevelNamespacedEnums.isEmpty else { return [] } + guard !namespacedEnums.isEmpty else { return [] } var lines: [String] = [] var uniqueNamespaces: [String] = [] var seen = Set() - for enumDef in topLevelNamespacedEnums { + for enumDef in namespacedEnums { guard let namespacePath = enumDef.namespace else { continue } namespacePath.enumerated().forEach { (index, _) in let path = namespacePath[0...index].joined(separator: ".") @@ -1080,7 +1339,7 @@ struct BridgeJSLink { lines.append("") } - for enumDef in topLevelNamespacedEnums { + for enumDef in namespacedEnums { let namespacePath = enumDef.namespace?.joined(separator: ".") ?? "" lines.append("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);") } @@ -1291,7 +1550,53 @@ struct BridgeJSLink { .indent(count: identBaseSize * contentDepth) ) } - case .associatedValue, .namespace: + case .associatedValue: + dtsLines.append( + "const \(enumDefinition.name): {".indent(count: identBaseSize * contentDepth) + ) + dtsLines.append("readonly Tag: {".indent(count: identBaseSize * (contentDepth + 1))) + for (caseIndex, enumCase) in enumDefinition.cases.enumerated() { + let caseName = enumCase.name.capitalizedFirstLetter + dtsLines.append( + "readonly \(caseName): \(caseIndex);".indent( + count: identBaseSize * (contentDepth + 2) + ) + ) + } + dtsLines.append("};".indent(count: identBaseSize * (contentDepth + 1))) + dtsLines.append("};".indent(count: identBaseSize * contentDepth)) + + var unionParts: [String] = [] + for enumCase in enumDefinition.cases { + if enumCase.associatedValues.isEmpty { + unionParts.append( + "{ tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter) }" + ) + } else { + var fields: [String] = [ + "tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter)" + ] + for (associatedValueIndex, associatedValue) in enumCase.associatedValues + .enumerated() + { + let prop = associatedValue.label ?? "param\(associatedValueIndex)" + let ts: String + switch associatedValue.type { + case .string: ts = "string" + case .bool: ts = "boolean" + case .int, .float, .double: ts = "number" + default: ts = "never" + } + fields.append("\(prop): \(ts)") + } + unionParts.append("{ \(fields.joined(separator: "; ")) }") + } + } + dtsLines.append("type \(enumDefinition.name) =".indent(count: identBaseSize * contentDepth)) + dtsLines.append( + " " + unionParts.joined(separator: " | ").indent(count: identBaseSize * contentDepth) + ) + case .namespace: continue } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index ae74844f..84417cd4 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -15,6 +15,12 @@ final class JSGlueVariableScope { static let reservedStorageToReturnException = "tmpRetException" static let reservedTextEncoder = "textEncoder" static let reservedTextDecoder = "textDecoder" + static let reservedTmpRetTag = "tmpRetTag" + static let reservedTmpRetStrings = "tmpRetStrings" + static let reservedTmpRetInts = "tmpRetInts" + static let reservedTmpRetF32s = "tmpRetF32s" + static let reservedTmpRetF64s = "tmpRetF64s" + static let reservedTmpRetBools = "tmpRetBools" private var variables: Set = [ reservedSwift, @@ -24,6 +30,12 @@ final class JSGlueVariableScope { reservedStorageToReturnException, reservedTextEncoder, reservedTextDecoder, + reservedTmpRetTag, + reservedTmpRetStrings, + reservedTmpRetInts, + reservedTmpRetF32s, + reservedTmpRetF64s, + reservedTmpRetBools, ] /// Returns a unique variable name in the scope based on the given name hint. @@ -194,6 +206,37 @@ struct IntrinsicJSFragment: Sendable { ) } + static func associatedEnumLowerParameter(enumBase: String) -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanup in + let value = arguments[0] + let caseIdName = "\(value)CaseId" + let paramsIdName = "\(value)ParamsId" + let paramsLenName = "\(value)ParamsLen" + let cleanupName = "\(value)Cleanup" + printer.write( + "const { caseId: \(caseIdName), paramsId: \(paramsIdName), paramsLen: \(paramsLenName), cleanup: \(cleanupName) } = enumHelpers.\(enumBase).lower(\(value));" + ) + cleanup.write("if (\(cleanupName)) { \(cleanupName)(); }") + return [caseIdName, paramsIdName, paramsLenName] + } + ) + } + + static func associatedEnumLiftReturn(enumBase: String) -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: [], + printCode: { _, scope, printer, _ in + let retName = scope.variable("ret") + printer.write( + "const \(retName) = enumHelpers.\(enumBase).raise(\(JSGlueVariableScope.reservedTmpRetTag), \(JSGlueVariableScope.reservedTmpRetStrings), \(JSGlueVariableScope.reservedTmpRetInts), \(JSGlueVariableScope.reservedTmpRetF32s), \(JSGlueVariableScope.reservedTmpRetF64s), \(JSGlueVariableScope.reservedTmpRetBools));" + ) + return [retName] + } + ) + } + // MARK: - ExportSwift /// Returns a fragment that lowers a JS value to Wasm core values for parameters @@ -211,10 +254,9 @@ struct IntrinsicJSFragment: Sendable { case .string: return .stringLowerParameter default: return .identity } - case .associatedValueEnum(let string): - throw BridgeJSLinkError( - message: "Associated value enums are not supported to be passed as parameters: \(string)" - ) + case .associatedValueEnum(let fullName): + let base = fullName.components(separatedBy: ".").last ?? fullName + return .associatedEnumLowerParameter(enumBase: base) case .namespaceEnum(let string): throw BridgeJSLinkError(message: "Namespace enums are not supported to be passed as parameters: \(string)") } @@ -236,10 +278,9 @@ struct IntrinsicJSFragment: Sendable { case .bool: return .boolLiftReturn default: return .identity } - case .associatedValueEnum(let string): - throw BridgeJSLinkError( - message: "Associated value enums are not supported to be returned from functions: \(string)" - ) + case .associatedValueEnum(let fullName): + let base = fullName.components(separatedBy: ".").last ?? fullName + return .associatedEnumLiftReturn(enumBase: base) case .namespaceEnum(let string): throw BridgeJSLinkError( message: "Namespace enums are not supported to be returned from functions: \(string)" diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift new file mode 100644 index 00000000..08d03a2b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift @@ -0,0 +1,40 @@ +@JS +enum APIResult { + case success(String) + case failure(Int) + case flag(Bool) + case rate(Float) + case precise(Double) + case info +} + +@JS func handle(result: APIResult) +@JS func getResult() -> APIResult + +@JS +enum ComplexResult { + case success(String) + case error(String, Int) + case status(Bool, Int, String) + case coordinates(Double, Double, Double) + case comprehensive(Bool, Bool, Int, Int, Double, Double, String, String, String) + case info +} + +@JS func handleComplex(result: ComplexResult) +@JS func getComplexResult() -> ComplexResult + +@JS +enum Utilities { + @JS enum Result { + case success(String) + case failure(String, Int) + case status(Bool, Int, String) + } +} + +@JS(namespace: "API") +@JS enum NetworkingResult { + case success(String) + case failure(String, Int) +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js index c122f179..602cd9a4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkArray"] = function bjs_checkArray(a) { @@ -76,6 +109,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js index 97c1e215..f20f4932 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js index 0b07aedd..95898a12 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid() { @@ -120,6 +153,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts new file mode 100644 index 00000000..4d7394b4 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts @@ -0,0 +1,75 @@ +// 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 const APIResult: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + readonly Flag: 2; + readonly Rate: 3; + readonly Precise: 4; + readonly Info: 5; + }; +}; + +export type APIResult = + { tag: typeof APIResult.Tag.Success; param0: string } | { tag: typeof APIResult.Tag.Failure; param0: number } | { tag: typeof APIResult.Tag.Flag; param0: boolean } | { tag: typeof APIResult.Tag.Rate; param0: number } | { tag: typeof APIResult.Tag.Precise; param0: number } | { tag: typeof APIResult.Tag.Info } + +export const ComplexResult: { + readonly Tag: { + readonly Success: 0; + readonly Error: 1; + readonly Status: 2; + readonly Coordinates: 3; + readonly Comprehensive: 4; + readonly Info: 5; + }; +}; + +export type ComplexResult = + { tag: typeof ComplexResult.Tag.Success; param0: string } | { tag: typeof ComplexResult.Tag.Error; param0: string; param1: number } | { tag: typeof ComplexResult.Tag.Status; param0: boolean; param1: number; param2: string } | { tag: typeof ComplexResult.Tag.Coordinates; param0: number; param1: number; param2: number } | { tag: typeof ComplexResult.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | { tag: typeof ComplexResult.Tag.Info } + +export {}; + +declare global { + namespace API { + const NetworkingResult: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + }; + }; + type NetworkingResult = + { tag: typeof NetworkingResult.Tag.Success; param0: string } | { tag: typeof NetworkingResult.Tag.Failure; param0: string; param1: number } + } + namespace Utilities { + const Result: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + readonly Status: 2; + }; + }; + type Result = + { tag: typeof Result.Tag.Success; param0: string } | { tag: typeof Result.Tag.Failure; param0: string; param1: number } | { tag: typeof Result.Tag.Status; param0: boolean; param1: number; param2: string } + } +} + +export type Exports = { + handle(result: APIResult): void; + getResult(): APIResult; + handleComplex(result: ComplexResult): void; + getComplexResult(): ComplexResult; +} +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/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js new file mode 100644 index 00000000..79a2e777 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -0,0 +1,445 @@ +// 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`. + +/// Shared helpers for encoding associated-value enums between JS and Swift. +const __bjs_ParamType = { STRING: 1, INT32: 2, BOOL: 3, FLOAT32: 4, FLOAT64: 5 }; +function __bjs_encodeEnumParams(textEncoder, swift, parts) { + const SIZE_U8 = 1, SIZE_U32 = 4, SIZE_F32 = 4, SIZE_F64 = 8; + let totalLen = SIZE_U32; + for (const p of parts) { + switch (p.t) { + case __bjs_ParamType.STRING: { + const bytes = textEncoder.encode(p.v); + p._bytes = bytes; + totalLen += SIZE_U8 + SIZE_U32 + bytes.length; + break; + } + case __bjs_ParamType.INT32: totalLen += SIZE_U8 + SIZE_U32; break; + case __bjs_ParamType.BOOL: totalLen += SIZE_U8 + SIZE_U8; break; + case __bjs_ParamType.FLOAT32: totalLen += SIZE_U8 + SIZE_F32; break; + case __bjs_ParamType.FLOAT64: totalLen += SIZE_U8 + SIZE_F64; break; + default: throw new Error("Unsupported param type tag: " + p.t); + } + } + const buf = new Uint8Array(totalLen); + const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + let off = 0; + view.setUint32(off, parts.length, true); off += SIZE_U32; + for (const p of parts) { + view.setUint8(off, p.t); off += SIZE_U8; + switch (p.t) { + case __bjs_ParamType.STRING: { + const b = p._bytes; + view.setUint32(off, b.length, true); off += SIZE_U32; + buf.set(b, off); off += b.length; + break; + } + case __bjs_ParamType.INT32: + view.setInt32(off, (p.v | 0), true); off += SIZE_U32; break; + case __bjs_ParamType.BOOL: + view.setUint8(off, p.v ? 1 : 0); off += SIZE_U8; break; + case __bjs_ParamType.FLOAT32: + view.setFloat32(off, Math.fround(p.v), true); off += SIZE_F32; break; + case __bjs_ParamType.FLOAT64: + view.setFloat64(off, p.v, true); off += SIZE_F64; break; + default: throw new Error("Unsupported param type tag: " + p.t); + } + } + const paramsId = swift.memory.retain(buf); + return { paramsId, paramsLen: buf.length, cleanup: () => { swift.memory.release(paramsId); } }; +} +export const APIResult = { + Tag: { + Success: 0, + Failure: 1, + Flag: 2, + Rate: 3, + Precise: 4, + Info: 5, + } +}; + +const __bjs_createAPIResultHelpers = () => { + return (textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResult.Tag.Success: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Success, paramsId, paramsLen, cleanup }; + } + case APIResult.Tag.Failure: { + const parts = [ + { t: __bjs_ParamType.INT32, v: (value.param0 | 0) } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Failure, paramsId, paramsLen, cleanup }; + } + case APIResult.Tag.Flag: { + const parts = [ + { t: __bjs_ParamType.BOOL, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Flag, paramsId, paramsLen, cleanup }; + } + case APIResult.Tag.Rate: { + const parts = [ + { t: __bjs_ParamType.FLOAT32, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Rate, paramsId, paramsLen, cleanup }; + } + case APIResult.Tag.Precise: { + const parts = [ + { t: __bjs_ParamType.FLOAT64, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Precise, paramsId, paramsLen, cleanup }; + } + case APIResult.Tag.Info: { + const parts = []; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: APIResult.Tag.Info, paramsId, paramsLen, cleanup }; + } + default: throw new Error("Unknown APIResult tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools) => { + const tag = tmpRetTag | 0; + switch (tag) { + case APIResult.Tag.Success: return { tag: APIResult.Tag.Success, param0: tmpRetStrings[0] }; + case APIResult.Tag.Failure: return { tag: APIResult.Tag.Failure, param0: tmpRetInts[0] }; + case APIResult.Tag.Flag: return { tag: APIResult.Tag.Flag, param0: tmpRetBools[0] }; + case APIResult.Tag.Rate: return { tag: APIResult.Tag.Rate, param0: tmpRetF32s[0] }; + case APIResult.Tag.Precise: return { tag: APIResult.Tag.Precise, param0: tmpRetF64s[0] }; + case APIResult.Tag.Info: return { tag: APIResult.Tag.Info }; + default: throw new Error("Unknown APIResult tag returned from Swift: " + String(tag)); + } + } + }); +}; +export const ComplexResult = { + Tag: { + Success: 0, + Error: 1, + Status: 2, + Coordinates: 3, + Comprehensive: 4, + Info: 5, + } +}; + +const __bjs_createComplexResultHelpers = () => { + return (textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case ComplexResult.Tag.Success: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Success, paramsId, paramsLen, cleanup }; + } + case ComplexResult.Tag.Error: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 }, + { t: __bjs_ParamType.INT32, v: (value.param1 | 0) } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Error, paramsId, paramsLen, cleanup }; + } + case ComplexResult.Tag.Status: { + const parts = [ + { t: __bjs_ParamType.BOOL, v: value.param0 }, + { t: __bjs_ParamType.INT32, v: (value.param1 | 0) }, + { t: __bjs_ParamType.STRING, v: value.param2 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Status, paramsId, paramsLen, cleanup }; + } + case ComplexResult.Tag.Coordinates: { + const parts = [ + { t: __bjs_ParamType.FLOAT64, v: value.param0 }, + { t: __bjs_ParamType.FLOAT64, v: value.param1 }, + { t: __bjs_ParamType.FLOAT64, v: value.param2 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Coordinates, paramsId, paramsLen, cleanup }; + } + case ComplexResult.Tag.Comprehensive: { + const parts = [ + { t: __bjs_ParamType.BOOL, v: value.param0 }, + { t: __bjs_ParamType.BOOL, v: value.param1 }, + { t: __bjs_ParamType.INT32, v: (value.param2 | 0) }, + { t: __bjs_ParamType.INT32, v: (value.param3 | 0) }, + { t: __bjs_ParamType.FLOAT64, v: value.param4 }, + { t: __bjs_ParamType.FLOAT64, v: value.param5 }, + { t: __bjs_ParamType.STRING, v: value.param6 }, + { t: __bjs_ParamType.STRING, v: value.param7 }, + { t: __bjs_ParamType.STRING, v: value.param8 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Comprehensive, paramsId, paramsLen, cleanup }; + } + case ComplexResult.Tag.Info: { + const parts = []; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: ComplexResult.Tag.Info, paramsId, paramsLen, cleanup }; + } + default: throw new Error("Unknown ComplexResult tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools) => { + const tag = tmpRetTag | 0; + switch (tag) { + case ComplexResult.Tag.Success: return { tag: ComplexResult.Tag.Success, param0: tmpRetStrings[0] }; + case ComplexResult.Tag.Error: return { tag: ComplexResult.Tag.Error, param0: tmpRetStrings[0], param1: tmpRetInts[0] }; + case ComplexResult.Tag.Status: return { tag: ComplexResult.Tag.Status, param0: tmpRetBools[0], param1: tmpRetInts[0], param2: tmpRetStrings[0] }; + case ComplexResult.Tag.Coordinates: return { tag: ComplexResult.Tag.Coordinates, param0: tmpRetF64s[0], param1: tmpRetF64s[1], param2: tmpRetF64s[2] }; + case ComplexResult.Tag.Comprehensive: return { tag: ComplexResult.Tag.Comprehensive, param0: tmpRetBools[0], param1: tmpRetBools[1], param2: tmpRetInts[0], param3: tmpRetInts[1], param4: tmpRetF64s[0], param5: tmpRetF64s[1], param6: tmpRetStrings[0], param7: tmpRetStrings[1], param8: tmpRetStrings[2] }; + case ComplexResult.Tag.Info: return { tag: ComplexResult.Tag.Info }; + default: throw new Error("Unknown ComplexResult tag returned from Swift: " + String(tag)); + } + } + }); +}; +export const Result = { + Tag: { + Success: 0, + Failure: 1, + Status: 2, + } +}; + +const __bjs_createResultHelpers = () => { + return (textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case Result.Tag.Success: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: Result.Tag.Success, paramsId, paramsLen, cleanup }; + } + case Result.Tag.Failure: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 }, + { t: __bjs_ParamType.INT32, v: (value.param1 | 0) } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: Result.Tag.Failure, paramsId, paramsLen, cleanup }; + } + case Result.Tag.Status: { + const parts = [ + { t: __bjs_ParamType.BOOL, v: value.param0 }, + { t: __bjs_ParamType.INT32, v: (value.param1 | 0) }, + { t: __bjs_ParamType.STRING, v: value.param2 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: Result.Tag.Status, paramsId, paramsLen, cleanup }; + } + default: throw new Error("Unknown Result tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools) => { + const tag = tmpRetTag | 0; + switch (tag) { + case Result.Tag.Success: return { tag: Result.Tag.Success, param0: tmpRetStrings[0] }; + case Result.Tag.Failure: return { tag: Result.Tag.Failure, param0: tmpRetStrings[0], param1: tmpRetInts[0] }; + case Result.Tag.Status: return { tag: Result.Tag.Status, param0: tmpRetBools[0], param1: tmpRetInts[0], param2: tmpRetStrings[0] }; + default: throw new Error("Unknown Result tag returned from Swift: " + String(tag)); + } + } + }); +}; +export const NetworkingResult = { + Tag: { + Success: 0, + Failure: 1, + } +}; + +const __bjs_createNetworkingResultHelpers = () => { + return (textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case NetworkingResult.Tag.Success: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: NetworkingResult.Tag.Success, paramsId, paramsLen, cleanup }; + } + case NetworkingResult.Tag.Failure: { + const parts = [ + { t: __bjs_ParamType.STRING, v: value.param0 }, + { t: __bjs_ParamType.INT32, v: (value.param1 | 0) } + ]; + const { paramsId, paramsLen, cleanup } = __bjs_encodeEnumParams(textEncoder, swift, parts); + return { caseId: NetworkingResult.Tag.Failure, paramsId, paramsLen, cleanup }; + } + default: throw new Error("Unknown NetworkingResult tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools) => { + const tag = tmpRetTag | 0; + switch (tag) { + case NetworkingResult.Tag.Success: return { tag: NetworkingResult.Tag.Success, param0: tmpRetStrings[0] }; + case NetworkingResult.Tag.Failure: return { tag: NetworkingResult.Tag.Failure, param0: tmpRetStrings[0], param1: tmpRetInts[0] }; + default: throw new Error("Unknown NetworkingResult tag returned from Swift: " + String(tag)); + } + } + }); +}; + +if (typeof globalThis.Utilities === 'undefined') { + globalThis.Utilities = {}; +} +if (typeof globalThis.API === 'undefined') { + globalThis.API = {}; +} + +globalThis.Utilities.Result = Result; +globalThis.API.NetworkingResult = NetworkingResult; + +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 tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + const enumHelpers = {}; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + const 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); + } + 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_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } + + + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + + const APIResultHelpers = __bjs_createAPIResultHelpers()(textEncoder, swift); + enumHelpers.APIResult = APIResultHelpers; + + const ComplexResultHelpers = __bjs_createComplexResultHelpers()(textEncoder, swift); + enumHelpers.ComplexResult = ComplexResultHelpers; + + const ResultHelpers = __bjs_createResultHelpers()(textEncoder, swift); + enumHelpers.Result = ResultHelpers; + + const NetworkingResultHelpers = __bjs_createNetworkingResultHelpers()(textEncoder, swift); + enumHelpers.NetworkingResult = NetworkingResultHelpers; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + + return { + handle: function bjs_handle(result) { + const { caseId: resultCaseId, paramsId: resultParamsId, paramsLen: resultParamsLen, cleanup: resultCleanup } = enumHelpers.APIResult.lower(result); + instance.exports.bjs_handle(resultCaseId, resultParamsId, resultParamsLen); + if (resultCleanup) { resultCleanup(); } + }, + getResult: function bjs_getResult() { + instance.exports.bjs_getResult(); + const ret = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools); + return ret; + }, + handleComplex: function bjs_handleComplex(result) { + const { caseId: resultCaseId, paramsId: resultParamsId, paramsLen: resultParamsLen, cleanup: resultCleanup } = enumHelpers.ComplexResult.lower(result); + instance.exports.bjs_handleComplex(resultCaseId, resultParamsId, resultParamsLen); + if (resultCleanup) { resultCleanup(); } + }, + getComplexResult: function bjs_getComplexResult() { + instance.exports.bjs_getComplexResult(); + const ret = enumHelpers.ComplexResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetBools); + return ret; + }, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js index b5bc1145..7b7a3003 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js @@ -35,10 +35,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -49,7 +56,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -74,12 +83,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js index 677e02c9..b6f5ff98 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js @@ -57,10 +57,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -71,7 +78,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -96,6 +105,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; @@ -117,6 +150,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js index 2be034b4..0d85607a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js @@ -86,10 +86,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -100,7 +107,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -125,12 +134,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js index f81c7e47..18fd6be1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_returnAnimatable"] = function bjs_returnAnimatable() { @@ -82,6 +115,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js index 6b5211e0..834f1a79 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createArrayBuffer"] = function bjs_createArrayBuffer() { @@ -152,6 +185,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js index d9a13b5e..d4e582ed 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createDatabaseConnection"] = function bjs_createDatabaseConnection(config) { @@ -186,6 +219,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js index 6915a61a..519058fa 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; @@ -70,6 +103,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js index 4873fc33..e10084e5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js index b0dbaa19..f6d572b1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_check"] = function bjs_check(a, b) { @@ -62,6 +95,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js index 594dc9d5..8fe8c446 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js index a61149cd..6dbbbac5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkNumber"] = function bjs_checkNumber() { @@ -73,6 +106,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js index ffc1eab6..f273ef10 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; @@ -62,6 +95,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js index ea47fb55..835eb802 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js index 16ed1081..9e28b776 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkString"] = function bjs_checkString(a) { @@ -73,6 +106,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js index f98cea55..43d2e4fb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js index 3220ae7b..0c9eccf8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkString"] = function bjs_checkString() { @@ -64,6 +97,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js index fb995d24..370c6229 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; @@ -70,6 +103,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js index c7671805..cb20a60b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createTS2Skeleton"] = function bjs_createTS2Skeleton() { @@ -124,6 +157,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js index b2089962..3836d437 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js index 2eb9dee5..578ec5d6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkSimple"] = function bjs_checkSimple(a) { @@ -62,6 +95,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js index 48d15c7e..bd6a6907 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_Greeter_init"] = function bjs_Greeter_init(name) { @@ -110,6 +143,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js index c200c077..91f069dd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,12 +58,37 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } }, setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js index ca497688..5eab9f77 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js @@ -10,10 +10,17 @@ export async function createInstantiator(options, swift) { let setException; const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpRetBools = []; + + return { /** * @param {WebAssembly.Imports} importObject @@ -24,7 +31,9 @@ export async function createInstantiator(options, swift) { 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); + const value = textDecoder.decode(bytes); + tmpRetString = value; + tmpRetStrings.push(value); } bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { const source = swift.memory.getObject(sourceId); @@ -49,6 +58,30 @@ export async function createInstantiator(options, swift) { bjs["swift_js_release"] = function(id) { swift.memory.release(id); } + bjs["swift_js_return_tag"] = function(tag) { + tmpRetTag = tag | 0; + tmpRetString = undefined; + tmpRetStrings = []; + tmpRetInts = []; + tmpRetF32s = []; + tmpRetF64s = []; + tmpRetBools = []; + } + bjs["swift_js_return_int"] = function(v) { + const value = v | 0; + tmpRetInts.push(value); + } + bjs["swift_js_return_f32"] = function(v) { + const value = Math.fround(v); + tmpRetF32s.push(value); + } + bjs["swift_js_return_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_return_bool"] = function(v) { + const value = v !== 0; + tmpRetBools.push(value); + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_check"] = function bjs_check() { @@ -62,6 +95,7 @@ export async function createInstantiator(options, swift) { setInstance: (i) => { instance = i; memory = instance.exports.memory; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json new file mode 100644 index 00000000..1eeca1b8 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -0,0 +1,435 @@ +{ + "classes" : [ + + ], + "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + } + ], + "name" : "flag" + }, + { + "associatedValues" : [ + { + "type" : { + "float" : { + + } + } + } + ], + "name" : "rate" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + } + ], + "name" : "precise" + }, + { + "associatedValues" : [ + + ], + "name" : "info" + } + ], + "emitStyle" : "const", + "name" : "APIResult", + "swiftCallName" : "APIResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "error" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "status" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + } + ], + "name" : "coordinates" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "comprehensive" + }, + { + "associatedValues" : [ + + ], + "name" : "info" + } + ], + "emitStyle" : "const", + "name" : "ComplexResult", + "swiftCallName" : "ComplexResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "status" + } + ], + "emitStyle" : "const", + "name" : "Result", + "namespace" : [ + "Utilities" + ], + "swiftCallName" : "Utilities.Result" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + } + ], + "emitStyle" : "const", + "name" : "NetworkingResult", + "namespace" : [ + "API" + ], + "swiftCallName" : "NetworkingResult" + } + ], + "functions" : [ + { + "abiName" : "bjs_handle", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "handle", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "getResult", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_handleComplex", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "handleComplex", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getComplexResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "getComplexResult", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift new file mode 100644 index 00000000..fa3cef9e --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift @@ -0,0 +1,348 @@ +// 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(BridgeJS) import JavaScriptKit + +private extension APIResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> APIResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 1) + reader.expectTag(.int32) + let param0 = Int(reader.readInt32()) + return .failure(param0) + case 2: + reader.readParamCount(expected: 1) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + return .flag(param0) + case 3: + reader.readParamCount(expected: 1) + reader.expectTag(.float32) + let param0 = reader.readFloat32() + return .rate(param0) + case 4: + reader.readParamCount(expected: 1) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + return .precise(param0) + case 5: + return .info + default: + fatalError("Unknown APIResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0): + _swift_js_return_tag(Int32(1)) + _swift_js_return_int(Int32(param0)) + case .flag(let param0): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + case .rate(let param0): + _swift_js_return_tag(Int32(3)) + _swift_js_return_f32(param0) + case .precise(let param0): + _swift_js_return_tag(Int32(4)) + _swift_js_return_f64(param0) + case .info: + _swift_js_return_tag(Int32(5)) + } + } +} + +private extension ComplexResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> ComplexResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .error(param0, param1) + case 2: + reader.readParamCount(expected: 3) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + reader.expectTag(.string) + let param2 = reader.readString() + return .status(param0, param1, param2) + case 3: + reader.readParamCount(expected: 3) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + reader.expectTag(.float64) + let param1 = reader.readFloat64() + reader.expectTag(.float64) + let param2 = reader.readFloat64() + return .coordinates(param0, param1, param2) + case 4: + reader.readParamCount(expected: 9) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.bool) + let param1 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param2 = Int(reader.readInt32()) + reader.expectTag(.int32) + let param3 = Int(reader.readInt32()) + reader.expectTag(.float64) + let param4 = reader.readFloat64() + reader.expectTag(.float64) + let param5 = reader.readFloat64() + reader.expectTag(.string) + let param6 = reader.readString() + reader.expectTag(.string) + let param7 = reader.readString() + reader.expectTag(.string) + let param8 = reader.readString() + return .comprehensive(param0, param1, param2, param3, param4, param5, param6, param7, param8) + case 5: + return .info + default: + fatalError("Unknown ComplexResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .error(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + case .status(let param0, let param1, let param2): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_int(Int32(param1)) + var __bjs_param2 = param2 + __bjs_param2.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .coordinates(let param0, let param1, let param2): + _swift_js_return_tag(Int32(3)) + _swift_js_return_f64(param0) + _swift_js_return_f64(param1) + _swift_js_return_f64(param2) + case .comprehensive(let param0, let param1, let param2, let param3, let param4, let param5, let param6, let param7, let param8): + _swift_js_return_tag(Int32(4)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_bool(param1 ? 1 : 0) + _swift_js_return_int(Int32(param2)) + _swift_js_return_int(Int32(param3)) + _swift_js_return_f64(param4) + _swift_js_return_f64(param5) + var __bjs_param6 = param6 + __bjs_param6.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + var __bjs_param7 = param7 + __bjs_param7.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + var __bjs_param8 = param8 + __bjs_param8.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .info: + _swift_js_return_tag(Int32(5)) + } + } +} + +private extension Utilities.Result { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> Utilities.Result { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .failure(param0, param1) + case 2: + reader.readParamCount(expected: 3) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + reader.expectTag(.string) + let param2 = reader.readString() + return .status(param0, param1, param2) + default: + fatalError("Unknown Utilities.Result case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + case .status(let param0, let param1, let param2): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_int(Int32(param1)) + var __bjs_param2 = param2 + __bjs_param2.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + } + } +} + +private extension NetworkingResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> NetworkingResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .failure(param0, param1) + default: + fatalError("Unknown NetworkingResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + } + } +} + +@_expose(wasm, "bjs_handle") +@_cdecl("bjs_handle") +public func _bjs_handle(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + handle(result: APIResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getResult") +@_cdecl("bjs_getResult") +public func _bjs_getResult() -> Void { + #if arch(wasm32) + let ret = getResult() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_handleComplex") +@_cdecl("bjs_handleComplex") +public func _bjs_handleComplex(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + handleComplex(result: ComplexResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getComplexResult") +@_cdecl("bjs_getComplexResult") +public func _bjs_getComplexResult() -> Void { + #if arch(wasm32) + let ret = getComplexResult() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift index bb0fbe43..23120ea7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift @@ -6,9 +6,6 @@ @_spi(BridgeJS) import JavaScriptKit -extension Utils: _BridgedSwiftEnumNoPayload { -} - extension Networking.API.Method { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index 56b87fa5..3107252d 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -338,3 +338,164 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo rawValue.bridgeJSLowerReturn() } } + +// MARK: Enum binary helpers + +@_spi(BridgeJS) public enum _BJSParamType: UInt8 { + case string = 1 + case int32 = 2 + case bool = 3 + case float32 = 4 + case float64 = 5 +} + +@_spi(BridgeJS) public struct _BJSBinaryReader { + public let raw: UnsafeRawBufferPointer + public var offset: Int = 0 + + public init(raw: UnsafeRawBufferPointer) { + self.raw = raw + } + + @inline(__always) + public mutating func readUInt8() -> UInt8 { + let b = raw[offset] + offset += 1 + return b + } + + @inline(__always) + public mutating func readUInt32() -> UInt32 { + var v = UInt32(0) + withUnsafeMutableBytes(of: &v) { dst in + dst.copyBytes(from: UnsafeRawBufferPointer(rebasing: raw[offset..<(offset + 4)])) + } + offset += 4 + return UInt32(littleEndian: v) + } + + @inline(__always) + public mutating func readInt32() -> Int32 { + var v = Int32(0) + withUnsafeMutableBytes(of: &v) { dst in + dst.copyBytes(from: UnsafeRawBufferPointer(rebasing: raw[offset..<(offset + 4)])) + } + offset += 4 + return Int32(littleEndian: v) + } + + @inline(__always) + public mutating func readFloat32() -> Float32 { + var bits = UInt32(0) + withUnsafeMutableBytes(of: &bits) { dst in + dst.copyBytes(from: UnsafeRawBufferPointer(rebasing: raw[offset..<(offset + 4)])) + } + offset += 4 + return Float32(bitPattern: UInt32(littleEndian: bits)) + } + + @inline(__always) + public mutating func readFloat64() -> Float64 { + var bits = UInt64(0) + withUnsafeMutableBytes(of: &bits) { dst in + dst.copyBytes(from: UnsafeRawBufferPointer(rebasing: raw[offset..<(offset + 8)])) + } + offset += 8 + return Float64(bitPattern: UInt64(littleEndian: bits)) + } + + @inline(__always) + public mutating func readString() -> String { + let len = Int(readUInt32()) + let s = String( + decoding: UnsafeBufferPointer( + start: raw.baseAddress!.advanced(by: offset).assumingMemoryBound(to: UInt8.self), + count: len + ), + as: UTF8.self + ) + offset += len + return s + } + + @inline(__always) + public mutating func expectTag(_ expected: _BJSParamType) { + let rawTag = readUInt8() + guard let got = _BJSParamType(rawValue: rawTag), got == expected else { + preconditionFailure( + "BridgeJS: mismatched enum param tag. Expected \(expected) got \(String(describing: _BJSParamType(rawValue: rawTag)))" + ) + } + } + + @inline(__always) + public mutating func readParamCount(expected: Int) { + let count = Int(readUInt32()) + precondition(count == expected, "BridgeJS: mismatched enum param count. Expected \(expected) got \(count)") + } +} + +// MARK: Wasm externs used by generated enum glue + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_init_memory") +@_spi(BridgeJS) public func _swift_js_init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer) +#else +@_spi(BridgeJS) public func _swift_js_init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_tag") +@_spi(BridgeJS) public func _swift_js_return_tag(_ tag: Int32) +#else +@_spi(BridgeJS) public func _swift_js_return_tag(_ tag: Int32) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_string") +@_spi(BridgeJS) public func _swift_js_return_string(_ ptr: UnsafePointer?, _ len: Int32) +#else +@_spi(BridgeJS) public func _swift_js_return_string(_ ptr: UnsafePointer?, _ len: Int32) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_int") +@_spi(BridgeJS) public func _swift_js_return_int(_ value: Int32) +#else +@_spi(BridgeJS) public func _swift_js_return_int(_ value: Int32) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_f32") +@_spi(BridgeJS) public func _swift_js_return_f32(_ value: Float32) +#else +@_spi(BridgeJS) public func _swift_js_return_f32(_ value: Float32) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_f64") +@_spi(BridgeJS) public func _swift_js_return_f64(_ value: Float64) +#else +@_spi(BridgeJS) public func _swift_js_return_f64(_ value: Float64) { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_return_bool") +@_spi(BridgeJS) public func _swift_js_return_bool(_ value: Int32) +#else +@_spi(BridgeJS) public func _swift_js_return_bool(_ value: Int32) { + _onlyAvailableOnWasm() +} +#endif diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md index e4bc1d7f..9c0f9971 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md @@ -405,4 +405,138 @@ enum Helper { #### Associated Value Enums -Associated value enums are not currently supported, but are planned for future releases. +Associated value enums are supported and allow you to pass data along with each enum case. BridgeJS generates TypeScript discriminated union types. Associated values are encoded into a binary format for efficient transfer between JavaScript and WebAssembly + +**Swift Definition:** + +```swift +@JS +enum APIResult { + case success(String) + case failure(Int) + case flag(Bool) + case rate(Float) + case precise(Double) + case info +} + +@JS +enum ComplexResult { + case success(String) + case error(String, Int) + case status(Bool, Int, String) + case coordinates(Double, Double, Double) + case comprehensive(Bool, Bool, Int, Int, Double, Double, String, String, String) + case info +} + +@JS func handle(result: APIResult) +@JS func getResult() -> APIResult +``` + +**Generated TypeScript Declaration:** + +```typescript +export const APIResult: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + readonly Flag: 2; + readonly Rate: 3; + readonly Precise: 4; + readonly Info: 5; + }; +}; + +export type APIResult = + { tag: typeof APIResult.Tag.Success; param0: string } | + { tag: typeof APIResult.Tag.Failure; param0: number } | + { tag: typeof APIResult.Tag.Flag; param0: boolean } | + { tag: typeof APIResult.Tag.Rate; param0: number } | + { tag: typeof APIResult.Tag.Precise; param0: number } | + { tag: typeof APIResult.Tag.Info } + +export const ComplexResult: { + readonly Tag: { + readonly Success: 0; + readonly Error: 1; + readonly Status: 2; + readonly Coordinates: 3; + readonly Comprehensive: 4; + readonly Info: 5; + }; +}; + +export type ComplexResult = + { tag: typeof ComplexResult.Tag.Success; param0: string } | + { tag: typeof ComplexResult.Tag.Error; param0: string; param1: number } | + { tag: typeof ComplexResult.Tag.Status; param0: boolean; param1: number; param2: string } | + { tag: typeof ComplexResult.Tag.Coordinates; param0: number; param1: number; param2: number } | + { tag: typeof ComplexResult.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | + { tag: typeof ComplexResult.Tag.Info } +``` + +**Usage in TypeScript:** + +```typescript +const successResult: APIResult = { + tag: exports.APIResult.Tag.Success, + param0: "Operation completed successfully" +}; + +const errorResult: ComplexResult = { + tag: exports.ComplexResult.Tag.Error, + param0: "Network timeout", + param1: 503 +}; + +const statusResult: ComplexResult = { + tag: exports.ComplexResult.Tag.Status, + param0: true, + param1: 200, + param2: "OK" +}; + +exports.handle(successResult); +exports.handle(errorResult); + +const result: APIResult = exports.getResult(); + +// Pattern matching with discriminated unions +function processResult(result: APIResult) { + switch (result.tag) { + case exports.APIResult.Tag.Success: + console.log("Success:", result.param0); // TypeScript knows param0 is string + break; + case exports.APIResult.Tag.Failure: + console.log("Failure code:", result.param0); // TypeScript knows param0 is number + break; + case exports.APIResult.Tag.Flag: + console.log("Flag value:", result.param0); // TypeScript knows param0 is boolean + break; + case exports.APIResult.Tag.Info: + console.log("Info case has no associated data"); + break; + // TypeScript will warn about missing cases + } +} +``` + +**Supported Features:** + +| Swift Feature | Status | +|:--------------|:-------| +| Associated values: `String` | ✅ | +| Associated values: `Int` | ✅ | +| Associated values: `Bool` | ✅ | +| Associated values: `Float` | ✅ | +| Associated values: `Double` | ✅ | +| Associated values: Custom classes/structs | ❌ | +| Associated values: Other enums | ❌ | +| Associated values: Arrays/Collections | ❌ | +| Associated values: Optionals | ❌ | +| Use as exported function parameters | ✅ | +| Use as exported function return values | ✅ | +| Use as imported function parameters | ❌ | +| Use as imported function return values | ❌ | +| Namespace support | ✅ | diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index ff42f3c7..995a856f 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -302,6 +302,143 @@ enum Internal { return method } +@JS enum APIResult { + case success(String) + case failure(Int) + case flag(Bool) + case rate(Float) + case precise(Double) + case info +} + +@JS func echoAPIResult(result: APIResult) -> APIResult { + return result +} + +@JS func makeAPIResultSuccess(_ value: String) -> APIResult { + return .success(value) +} + +@JS func makeAPIResultFailure(_ value: Int) -> APIResult { + return .failure(value) +} + +@JS func makeAPIResultInfo() -> APIResult { + return .info +} + +@JS func makeAPIResultFlag(_ value: Bool) -> APIResult { + return .flag(value) +} + +@JS func makeAPIResultRate(_ value: Float) -> APIResult { + return .rate(value) +} + +@JS func makeAPIResultPrecise(_ value: Double) -> APIResult { + return .precise(value) +} + +@JS +enum ComplexResult { + case success(String) + case error(String, Int) + case location(Double, Double, String) + case status(Bool, Int, String) + case coordinates(Double, Double, Double) + case comprehensive(Bool, Bool, Int, Int, Double, Double, String, String, String) + case info +} + +@JS func echoComplexResult(result: ComplexResult) -> ComplexResult { + return result +} + +@JS func makeComplexResultSuccess(_ value: String) -> ComplexResult { + return .success(value) +} + +@JS func makeComplexResultError(_ message: String, _ code: Int) -> ComplexResult { + return .error(message, code) +} + +@JS func makeComplexResultLocation(_ lat: Double, _ lng: Double, _ name: String) -> ComplexResult { + return .location(lat, lng, name) +} + +@JS func makeComplexResultStatus(_ active: Bool, _ code: Int, _ message: String) -> ComplexResult { + return .status(active, code, message) +} + +@JS func makeComplexResultCoordinates(_ x: Double, _ y: Double, _ z: Double) -> ComplexResult { + return .coordinates(x, y, z) +} + +@JS func makeComplexResultComprehensive( + _ flag1: Bool, + _ flag2: Bool, + _ count1: Int, + _ count2: Int, + _ value1: Double, + _ value2: Double, + _ text1: String, + _ text2: String, + _ text3: String +) -> ComplexResult { + return .comprehensive(flag1, flag2, count1, count2, value1, value2, text1, text2, text3) +} + +@JS func makeComplexResultInfo() -> ComplexResult { + return .info +} + +@JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult { + return result +} + +@JS enum Utilities { + @JS enum Result { + case success(String) + case failure(String, Int) + case status(Bool, Int, String) + } +} + +@JS enum API { + @JS enum NetworkingResult { + case success(String) + case failure(String, Int) + } +} + +@JS func makeUtilitiesResultSuccess(_ message: String) -> Utilities.Result { + return .success(message) +} + +@JS func makeUtilitiesResultFailure(_ error: String, _ code: Int) -> Utilities.Result { + return .failure(error, code) +} + +@JS func makeUtilitiesResultStatus(_ active: Bool, _ code: Int, _ message: String) -> Utilities.Result { + return .status(active, code, message) +} + +@JS func makeAPINetworkingResultSuccess(_ message: String) -> API.NetworkingResult { + return .success(message) +} + +@JS func makeAPINetworkingResultFailure(_ error: String, _ code: Int) -> API.NetworkingResult { + return .failure(error, code) +} + +@JS func roundtripUtilitiesResult(_ result: Utilities.Result) -> Utilities.Result { + return result +} + +@JS func roundtripAPINetworkingResult(_ result: API.NetworkingResult) -> API.NetworkingResult { + return result +} + // MARK: - Property Tests // Simple class for SwiftHeapObject property testing diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 2c70c647..6b22899c 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -140,9 +140,6 @@ extension TSDirection { extension TSTheme: _BridgedSwiftEnumNoPayload { } -extension Utils: _BridgedSwiftEnumNoPayload { -} - extension Networking.API.Method { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue @@ -227,6 +224,322 @@ extension Internal.SupportedMethod { } } +private extension APIResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> APIResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 1) + reader.expectTag(.int32) + let param0 = Int(reader.readInt32()) + return .failure(param0) + case 2: + reader.readParamCount(expected: 1) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + return .flag(param0) + case 3: + reader.readParamCount(expected: 1) + reader.expectTag(.float32) + let param0 = reader.readFloat32() + return .rate(param0) + case 4: + reader.readParamCount(expected: 1) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + return .precise(param0) + case 5: + return .info + default: + fatalError("Unknown APIResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0): + _swift_js_return_tag(Int32(1)) + _swift_js_return_int(Int32(param0)) + case .flag(let param0): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + case .rate(let param0): + _swift_js_return_tag(Int32(3)) + _swift_js_return_f32(param0) + case .precise(let param0): + _swift_js_return_tag(Int32(4)) + _swift_js_return_f64(param0) + case .info: + _swift_js_return_tag(Int32(5)) + } + } +} + +private extension ComplexResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> ComplexResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .error(param0, param1) + case 2: + reader.readParamCount(expected: 3) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + reader.expectTag(.float64) + let param1 = reader.readFloat64() + reader.expectTag(.string) + let param2 = reader.readString() + return .location(param0, param1, param2) + case 3: + reader.readParamCount(expected: 3) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + reader.expectTag(.string) + let param2 = reader.readString() + return .status(param0, param1, param2) + case 4: + reader.readParamCount(expected: 3) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + reader.expectTag(.float64) + let param1 = reader.readFloat64() + reader.expectTag(.float64) + let param2 = reader.readFloat64() + return .coordinates(param0, param1, param2) + case 5: + reader.readParamCount(expected: 9) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.bool) + let param1 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param2 = Int(reader.readInt32()) + reader.expectTag(.int32) + let param3 = Int(reader.readInt32()) + reader.expectTag(.float64) + let param4 = reader.readFloat64() + reader.expectTag(.float64) + let param5 = reader.readFloat64() + reader.expectTag(.string) + let param6 = reader.readString() + reader.expectTag(.string) + let param7 = reader.readString() + reader.expectTag(.string) + let param8 = reader.readString() + return .comprehensive(param0, param1, param2, param3, param4, param5, param6, param7, param8) + case 6: + return .info + default: + fatalError("Unknown ComplexResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .error(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + case .location(let param0, let param1, let param2): + _swift_js_return_tag(Int32(2)) + _swift_js_return_f64(param0) + _swift_js_return_f64(param1) + var __bjs_param2 = param2 + __bjs_param2.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .status(let param0, let param1, let param2): + _swift_js_return_tag(Int32(3)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_int(Int32(param1)) + var __bjs_param2 = param2 + __bjs_param2.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .coordinates(let param0, let param1, let param2): + _swift_js_return_tag(Int32(4)) + _swift_js_return_f64(param0) + _swift_js_return_f64(param1) + _swift_js_return_f64(param2) + case .comprehensive(let param0, let param1, let param2, let param3, let param4, let param5, let param6, let param7, let param8): + _swift_js_return_tag(Int32(5)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_bool(param1 ? 1 : 0) + _swift_js_return_int(Int32(param2)) + _swift_js_return_int(Int32(param3)) + _swift_js_return_f64(param4) + _swift_js_return_f64(param5) + var __bjs_param6 = param6 + __bjs_param6.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + var __bjs_param7 = param7 + __bjs_param7.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + var __bjs_param8 = param8 + __bjs_param8.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .info: + _swift_js_return_tag(Int32(6)) + } + } +} + +private extension Utilities.Result { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> Utilities.Result { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .failure(param0, param1) + case 2: + reader.readParamCount(expected: 3) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + reader.expectTag(.string) + let param2 = reader.readString() + return .status(param0, param1, param2) + default: + fatalError("Unknown Utilities.Result case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + case .status(let param0, let param1, let param2): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + _swift_js_return_int(Int32(param1)) + var __bjs_param2 = param2 + __bjs_param2.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + } + } +} + +private extension API.NetworkingResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> API.NetworkingResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 2) + reader.expectTag(.string) + let param0 = reader.readString() + reader.expectTag(.int32) + let param1 = Int(reader.readInt32()) + return .failure(param0, param1) + default: + fatalError("Unknown API.NetworkingResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0, let param1): + _swift_js_return_tag(Int32(1)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + _swift_js_return_int(Int32(param1)) + } + } +} + @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { @@ -869,6 +1182,259 @@ public func _bjs_echoInternalSupportedMethod(method: Int32) -> Int32 { #endif } +@_expose(wasm, "bjs_echoAPIResult") +@_cdecl("bjs_echoAPIResult") +public func _bjs_echoAPIResult(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + let ret = echoAPIResult(result: APIResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultSuccess") +@_cdecl("bjs_makeAPIResultSuccess") +public func _bjs_makeAPIResultSuccess(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeAPIResultSuccess(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultFailure") +@_cdecl("bjs_makeAPIResultFailure") +public func _bjs_makeAPIResultFailure(value: Int32) -> Void { + #if arch(wasm32) + let ret = makeAPIResultFailure(_: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultInfo") +@_cdecl("bjs_makeAPIResultInfo") +public func _bjs_makeAPIResultInfo() -> Void { + #if arch(wasm32) + let ret = makeAPIResultInfo() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultFlag") +@_cdecl("bjs_makeAPIResultFlag") +public func _bjs_makeAPIResultFlag(value: Int32) -> Void { + #if arch(wasm32) + let ret = makeAPIResultFlag(_: Bool.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultRate") +@_cdecl("bjs_makeAPIResultRate") +public func _bjs_makeAPIResultRate(value: Float32) -> Void { + #if arch(wasm32) + let ret = makeAPIResultRate(_: Float.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPIResultPrecise") +@_cdecl("bjs_makeAPIResultPrecise") +public func _bjs_makeAPIResultPrecise(value: Float64) -> Void { + #if arch(wasm32) + let ret = makeAPIResultPrecise(_: Double.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_echoComplexResult") +@_cdecl("bjs_echoComplexResult") +public func _bjs_echoComplexResult(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + let ret = echoComplexResult(result: ComplexResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultSuccess") +@_cdecl("bjs_makeComplexResultSuccess") +public func _bjs_makeComplexResultSuccess(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeComplexResultSuccess(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultError") +@_cdecl("bjs_makeComplexResultError") +public func _bjs_makeComplexResultError(messageBytes: Int32, messageLength: Int32, code: Int32) -> Void { + #if arch(wasm32) + let ret = makeComplexResultError(_: String.bridgeJSLiftParameter(messageBytes, messageLength), _: Int.bridgeJSLiftParameter(code)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultLocation") +@_cdecl("bjs_makeComplexResultLocation") +public func _bjs_makeComplexResultLocation(lat: Float64, lng: Float64, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeComplexResultLocation(_: Double.bridgeJSLiftParameter(lat), _: Double.bridgeJSLiftParameter(lng), _: String.bridgeJSLiftParameter(nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultStatus") +@_cdecl("bjs_makeComplexResultStatus") +public func _bjs_makeComplexResultStatus(active: Int32, code: Int32, messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeComplexResultStatus(_: Bool.bridgeJSLiftParameter(active), _: Int.bridgeJSLiftParameter(code), _: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultCoordinates") +@_cdecl("bjs_makeComplexResultCoordinates") +public func _bjs_makeComplexResultCoordinates(x: Float64, y: Float64, z: Float64) -> Void { + #if arch(wasm32) + let ret = makeComplexResultCoordinates(_: Double.bridgeJSLiftParameter(x), _: Double.bridgeJSLiftParameter(y), _: Double.bridgeJSLiftParameter(z)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultComprehensive") +@_cdecl("bjs_makeComplexResultComprehensive") +public func _bjs_makeComplexResultComprehensive(flag1: Int32, flag2: Int32, count1: Int32, count2: Int32, value1: Float64, value2: Float64, text1Bytes: Int32, text1Length: Int32, text2Bytes: Int32, text2Length: Int32, text3Bytes: Int32, text3Length: Int32) -> Void { + #if arch(wasm32) + let ret = makeComplexResultComprehensive(_: Bool.bridgeJSLiftParameter(flag1), _: Bool.bridgeJSLiftParameter(flag2), _: Int.bridgeJSLiftParameter(count1), _: Int.bridgeJSLiftParameter(count2), _: Double.bridgeJSLiftParameter(value1), _: Double.bridgeJSLiftParameter(value2), _: String.bridgeJSLiftParameter(text1Bytes, text1Length), _: String.bridgeJSLiftParameter(text2Bytes, text2Length), _: String.bridgeJSLiftParameter(text3Bytes, text3Length)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeComplexResultInfo") +@_cdecl("bjs_makeComplexResultInfo") +public func _bjs_makeComplexResultInfo() -> Void { + #if arch(wasm32) + let ret = makeComplexResultInfo() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundtripComplexResult") +@_cdecl("bjs_roundtripComplexResult") +public func _bjs_roundtripComplexResult(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + let ret = roundtripComplexResult(_: ComplexResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeUtilitiesResultSuccess") +@_cdecl("bjs_makeUtilitiesResultSuccess") +public func _bjs_makeUtilitiesResultSuccess(messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeUtilitiesResultSuccess(_: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeUtilitiesResultFailure") +@_cdecl("bjs_makeUtilitiesResultFailure") +public func _bjs_makeUtilitiesResultFailure(errorBytes: Int32, errorLength: Int32, code: Int32) -> Void { + #if arch(wasm32) + let ret = makeUtilitiesResultFailure(_: String.bridgeJSLiftParameter(errorBytes, errorLength), _: Int.bridgeJSLiftParameter(code)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeUtilitiesResultStatus") +@_cdecl("bjs_makeUtilitiesResultStatus") +public func _bjs_makeUtilitiesResultStatus(active: Int32, code: Int32, messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeUtilitiesResultStatus(_: Bool.bridgeJSLiftParameter(active), _: Int.bridgeJSLiftParameter(code), _: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPINetworkingResultSuccess") +@_cdecl("bjs_makeAPINetworkingResultSuccess") +public func _bjs_makeAPINetworkingResultSuccess(messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = makeAPINetworkingResultSuccess(_: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeAPINetworkingResultFailure") +@_cdecl("bjs_makeAPINetworkingResultFailure") +public func _bjs_makeAPINetworkingResultFailure(errorBytes: Int32, errorLength: Int32, code: Int32) -> Void { + #if arch(wasm32) + let ret = makeAPINetworkingResultFailure(_: String.bridgeJSLiftParameter(errorBytes, errorLength), _: Int.bridgeJSLiftParameter(code)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundtripUtilitiesResult") +@_cdecl("bjs_roundtripUtilitiesResult") +public func _bjs_roundtripUtilitiesResult(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + let ret = roundtripUtilitiesResult(_: Utilities.Result.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundtripAPINetworkingResult") +@_cdecl("bjs_roundtripAPINetworkingResult") +public func _bjs_roundtripAPINetworkingResult(resultCaseId: Int32, resultParamsId: Int32, resultParamsLen: Int32) -> Void { + #if arch(wasm32) + let ret = roundtripAPINetworkingResult(_: API.NetworkingResult.bridgeJSLiftParameter(resultCaseId, resultParamsId, resultParamsLen)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_createPropertyHolder") @_cdecl("bjs_createPropertyHolder") public func _bjs_createPropertyHolder(intValue: Int32, floatValue: Float32, doubleValue: Float64, boolValue: Int32, stringValueBytes: Int32, stringValueLength: Int32, jsObject: Int32) -> UnsafeMutableRawPointer { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index 8e65a50d..c1638c67 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -913,16 +913,707 @@ "Internal" ], "swiftCallName" : "Internal.SupportedMethod" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + } + ], + "name" : "flag" + }, + { + "associatedValues" : [ + { + "type" : { + "float" : { + + } + } + } + ], + "name" : "rate" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + } + ], + "name" : "precise" + }, + { + "associatedValues" : [ + + ], + "name" : "info" + } + ], + "emitStyle" : "const", + "name" : "APIResult", + "swiftCallName" : "APIResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "error" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "location" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "status" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + } + ], + "name" : "coordinates" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "double" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "comprehensive" + }, + { + "associatedValues" : [ + + ], + "name" : "info" + } + ], + "emitStyle" : "const", + "name" : "ComplexResult", + "swiftCallName" : "ComplexResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + }, + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "status" + } + ], + "emitStyle" : "const", + "name" : "Result", + "namespace" : [ + "Utilities" + ], + "swiftCallName" : "Utilities.Result" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + }, + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + } + ], + "emitStyle" : "const", + "name" : "NetworkingResult", + "namespace" : [ + "API" + ], + "swiftCallName" : "API.NetworkingResult" } ], "functions" : [ { - "abiName" : "bjs_roundTripVoid", + "abiName" : "bjs_roundTripVoid", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripVoid", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_roundTripInt", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripInt", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_roundTripFloat", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripFloat", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "float" : { + + } + } + } + ], + "returnType" : { + "float" : { + + } + } + }, + { + "abiName" : "bjs_roundTripDouble", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripDouble", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_roundTripBool", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripBool", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_roundTripString", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripString", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_roundTripSwiftHeapObject", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripSwiftHeapObject", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + }, + { + "abiName" : "bjs_roundTripJSObject", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripJSObject", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "jsObject" : { + + } + } + }, + { + "abiName" : "bjs_throwsSwiftError", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsSwiftError", + "parameters" : [ + { + "label" : "shouldThrow", + "name" : "shouldThrow", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithIntResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithIntResult", + "parameters" : [ + + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithStringResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithStringResult", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithBoolResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithBoolResult", + "parameters" : [ + + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithFloatResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithFloatResult", + "parameters" : [ + + ], + "returnType" : { + "float" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithDoubleResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithDoubleResult", + "parameters" : [ + + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_throwsWithSwiftHeapObjectResult", + "effects" : { + "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithSwiftHeapObjectResult", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + }, + { + "abiName" : "bjs_throwsWithJSObjectResult", "effects" : { "isAsync" : false, + "isThrows" : true + }, + "name" : "throwsWithJSObjectResult", + "parameters" : [ + + ], + "returnType" : { + "jsObject" : { + + } + } + }, + { + "abiName" : "bjs_asyncRoundTripVoid", + "effects" : { + "isAsync" : true, "isThrows" : false }, - "name" : "roundTripVoid", + "name" : "asyncRoundTripVoid", "parameters" : [ ], @@ -933,12 +1624,12 @@ } }, { - "abiName" : "bjs_roundTripInt", + "abiName" : "bjs_asyncRoundTripInt", "effects" : { - "isAsync" : false, + "isAsync" : true, "isThrows" : false }, - "name" : "roundTripInt", + "name" : "asyncRoundTripInt", "parameters" : [ { "label" : "v", @@ -957,12 +1648,12 @@ } }, { - "abiName" : "bjs_roundTripFloat", + "abiName" : "bjs_asyncRoundTripFloat", "effects" : { - "isAsync" : false, + "isAsync" : true, "isThrows" : false }, - "name" : "roundTripFloat", + "name" : "asyncRoundTripFloat", "parameters" : [ { "label" : "v", @@ -970,985 +1661,1372 @@ "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" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "takeGreeter", + "parameters" : [ + { + "label" : "g", + "name" : "g", + "type" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + }, + { + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_createCalculator", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "createCalculator", + "parameters" : [ + + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "Calculator" + } + } + }, + { + "abiName" : "bjs_useCalculator", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "useCalculator", + "parameters" : [ + { + "label" : "calc", + "name" : "calc", + "type" : { + "swiftHeapObject" : { + "_0" : "Calculator" + } + } + }, + { + "label" : "x", + "name" : "x", + "type" : { + "int" : { + + } + } + }, + { + "label" : "y", + "name" : "y", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_testGreeterToJSValue", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "testGreeterToJSValue", + "parameters" : [ + ], "returnType" : { - "float" : { + "jsObject" : { } } }, { - "abiName" : "bjs_roundTripDouble", + "abiName" : "bjs_testCalculatorToJSValue", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "roundTripDouble", + "name" : "testCalculatorToJSValue", "parameters" : [ - { - "label" : "v", - "name" : "v", - "type" : { - "double" : { - } - } - } ], "returnType" : { - "double" : { + "jsObject" : { } } }, { - "abiName" : "bjs_roundTripBool", + "abiName" : "bjs_testSwiftClassAsJSValue", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "roundTripBool", + "name" : "testSwiftClassAsJSValue", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "greeter", + "name" : "greeter", "type" : { - "bool" : { - + "swiftHeapObject" : { + "_0" : "Greeter" } } } ], "returnType" : { - "bool" : { + "jsObject" : { } } }, { - "abiName" : "bjs_roundTripString", + "abiName" : "bjs_setDirection", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "roundTripString", + "name" : "setDirection", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "direction", "type" : { - "string" : { - + "caseEnum" : { + "_0" : "Direction" } } } ], "returnType" : { - "string" : { - + "caseEnum" : { + "_0" : "Direction" } } }, { - "abiName" : "bjs_roundTripSwiftHeapObject", + "abiName" : "bjs_getDirection", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "roundTripSwiftHeapObject", + "name" : "getDirection", "parameters" : [ - { - "label" : "v", - "name" : "v", - "type" : { - "swiftHeapObject" : { - "_0" : "Greeter" - } - } - } + ], "returnType" : { - "swiftHeapObject" : { - "_0" : "Greeter" + "caseEnum" : { + "_0" : "Direction" } } }, { - "abiName" : "bjs_roundTripJSObject", + "abiName" : "bjs_processDirection", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "roundTripJSObject", + "name" : "processDirection", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "input", "type" : { - "jsObject" : { - + "caseEnum" : { + "_0" : "Direction" } } } ], "returnType" : { - "jsObject" : { - + "caseEnum" : { + "_0" : "Status" } } }, { - "abiName" : "bjs_throwsSwiftError", + "abiName" : "bjs_setTheme", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsSwiftError", + "name" : "setTheme", "parameters" : [ { - "label" : "shouldThrow", - "name" : "shouldThrow", + "label" : "_", + "name" : "theme", "type" : { - "bool" : { - + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" } } } ], "returnType" : { - "void" : { - + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" } } }, { - "abiName" : "bjs_throwsWithIntResult", + "abiName" : "bjs_getTheme", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithIntResult", + "name" : "getTheme", "parameters" : [ ], "returnType" : { - "int" : { - + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" } } }, { - "abiName" : "bjs_throwsWithStringResult", + "abiName" : "bjs_setHttpStatus", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithStringResult", + "name" : "setHttpStatus", "parameters" : [ - + { + "label" : "_", + "name" : "status", + "type" : { + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" + } + } + } ], "returnType" : { - "string" : { - + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" } } }, { - "abiName" : "bjs_throwsWithBoolResult", + "abiName" : "bjs_getHttpStatus", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithBoolResult", + "name" : "getHttpStatus", "parameters" : [ ], "returnType" : { - "bool" : { - + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" } } }, { - "abiName" : "bjs_throwsWithFloatResult", + "abiName" : "bjs_processTheme", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithFloatResult", + "name" : "processTheme", "parameters" : [ - + { + "label" : "_", + "name" : "theme", + "type" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } ], "returnType" : { - "float" : { - + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" } } }, { - "abiName" : "bjs_throwsWithDoubleResult", + "abiName" : "bjs_setTSDirection", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithDoubleResult", + "name" : "setTSDirection", "parameters" : [ - + { + "label" : "_", + "name" : "direction", + "type" : { + "caseEnum" : { + "_0" : "TSDirection" + } + } + } ], "returnType" : { - "double" : { - + "caseEnum" : { + "_0" : "TSDirection" } } }, { - "abiName" : "bjs_throwsWithSwiftHeapObjectResult", + "abiName" : "bjs_getTSDirection", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithSwiftHeapObjectResult", + "name" : "getTSDirection", "parameters" : [ ], "returnType" : { - "swiftHeapObject" : { - "_0" : "Greeter" + "caseEnum" : { + "_0" : "TSDirection" } } }, { - "abiName" : "bjs_throwsWithJSObjectResult", + "abiName" : "bjs_setTSTheme", "effects" : { "isAsync" : false, - "isThrows" : true + "isThrows" : false }, - "name" : "throwsWithJSObjectResult", + "name" : "setTSTheme", "parameters" : [ - + { + "label" : "_", + "name" : "theme", + "type" : { + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" + } + } + } ], "returnType" : { - "jsObject" : { - + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" } } }, { - "abiName" : "bjs_asyncRoundTripVoid", + "abiName" : "bjs_getTSTheme", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripVoid", + "name" : "getTSTheme", "parameters" : [ ], "returnType" : { - "void" : { - + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" } } }, { - "abiName" : "bjs_asyncRoundTripInt", + "abiName" : "bjs_echoNetworkingAPIMethod", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripInt", + "name" : "echoNetworkingAPIMethod", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "method", "type" : { - "int" : { - + "caseEnum" : { + "_0" : "Networking.API.Method" } } } ], "returnType" : { - "int" : { - + "caseEnum" : { + "_0" : "Networking.API.Method" } } }, { - "abiName" : "bjs_asyncRoundTripFloat", + "abiName" : "bjs_echoConfigurationLogLevel", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripFloat", + "name" : "echoConfigurationLogLevel", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "level", "type" : { - "float" : { - + "rawValueEnum" : { + "_0" : "Configuration.LogLevel", + "_1" : "String" } } } ], "returnType" : { - "float" : { - + "rawValueEnum" : { + "_0" : "Configuration.LogLevel", + "_1" : "String" } } }, { - "abiName" : "bjs_asyncRoundTripDouble", + "abiName" : "bjs_echoConfigurationPort", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripDouble", + "name" : "echoConfigurationPort", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "port", "type" : { - "double" : { - + "rawValueEnum" : { + "_0" : "Configuration.Port", + "_1" : "Int" } } } ], "returnType" : { - "double" : { - + "rawValueEnum" : { + "_0" : "Configuration.Port", + "_1" : "Int" } } }, { - "abiName" : "bjs_asyncRoundTripBool", + "abiName" : "bjs_processConfigurationLogLevel", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripBool", + "name" : "processConfigurationLogLevel", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "level", "type" : { - "bool" : { - + "rawValueEnum" : { + "_0" : "Configuration.LogLevel", + "_1" : "String" } } } ], "returnType" : { - "bool" : { - + "rawValueEnum" : { + "_0" : "Configuration.Port", + "_1" : "Int" } } }, { - "abiName" : "bjs_asyncRoundTripString", + "abiName" : "bjs_echoInternalSupportedMethod", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripString", + "name" : "echoInternalSupportedMethod", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "method", "type" : { - "string" : { - + "caseEnum" : { + "_0" : "Internal.SupportedMethod" } } } ], "returnType" : { - "string" : { - + "caseEnum" : { + "_0" : "Internal.SupportedMethod" } } }, { - "abiName" : "bjs_asyncRoundTripSwiftHeapObject", + "abiName" : "bjs_echoAPIResult", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripSwiftHeapObject", + "name" : "echoAPIResult", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "result", + "name" : "result", "type" : { - "swiftHeapObject" : { - "_0" : "Greeter" + "associatedValueEnum" : { + "_0" : "APIResult" } } } ], "returnType" : { - "swiftHeapObject" : { - "_0" : "Greeter" + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_asyncRoundTripJSObject", + "abiName" : "bjs_makeAPIResultSuccess", "effects" : { - "isAsync" : true, + "isAsync" : false, "isThrows" : false }, - "name" : "asyncRoundTripJSObject", + "name" : "makeAPIResultSuccess", "parameters" : [ { - "label" : "v", - "name" : "v", + "label" : "_", + "name" : "value", "type" : { - "jsObject" : { + "string" : { } } } ], "returnType" : { - "jsObject" : { - + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_takeGreeter", + "abiName" : "bjs_makeAPIResultFailure", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "takeGreeter", + "name" : "makeAPIResultFailure", "parameters" : [ { - "label" : "g", - "name" : "g", - "type" : { - "swiftHeapObject" : { - "_0" : "Greeter" - } - } - }, - { - "label" : "name", - "name" : "name", + "label" : "_", + "name" : "value", "type" : { - "string" : { + "int" : { } } } ], "returnType" : { - "void" : { - + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_createCalculator", + "abiName" : "bjs_makeAPIResultInfo", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "createCalculator", + "name" : "makeAPIResultInfo", "parameters" : [ ], "returnType" : { - "swiftHeapObject" : { - "_0" : "Calculator" + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_useCalculator", + "abiName" : "bjs_makeAPIResultFlag", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "useCalculator", + "name" : "makeAPIResultFlag", "parameters" : [ { - "label" : "calc", - "name" : "calc", - "type" : { - "swiftHeapObject" : { - "_0" : "Calculator" - } - } - }, - { - "label" : "x", - "name" : "x", - "type" : { - "int" : { - - } - } - }, - { - "label" : "y", - "name" : "y", + "label" : "_", + "name" : "value", "type" : { - "int" : { + "bool" : { } } } ], "returnType" : { - "int" : { - + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_testGreeterToJSValue", + "abiName" : "bjs_makeAPIResultRate", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "testGreeterToJSValue", + "name" : "makeAPIResultRate", "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "float" : { + } + } + } ], "returnType" : { - "jsObject" : { - + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_testCalculatorToJSValue", + "abiName" : "bjs_makeAPIResultPrecise", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "testCalculatorToJSValue", + "name" : "makeAPIResultPrecise", "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "double" : { + } + } + } ], "returnType" : { - "jsObject" : { - + "associatedValueEnum" : { + "_0" : "APIResult" } } }, { - "abiName" : "bjs_testSwiftClassAsJSValue", + "abiName" : "bjs_echoComplexResult", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "testSwiftClassAsJSValue", + "name" : "echoComplexResult", "parameters" : [ { - "label" : "greeter", - "name" : "greeter", + "label" : "result", + "name" : "result", "type" : { - "swiftHeapObject" : { - "_0" : "Greeter" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } } ], "returnType" : { - "jsObject" : { - + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_setDirection", + "abiName" : "bjs_makeComplexResultSuccess", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "setDirection", + "name" : "makeComplexResultSuccess", "parameters" : [ { "label" : "_", - "name" : "direction", + "name" : "value", "type" : { - "caseEnum" : { - "_0" : "Direction" + "string" : { + } } } ], "returnType" : { - "caseEnum" : { - "_0" : "Direction" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_getDirection", + "abiName" : "bjs_makeComplexResultError", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "getDirection", + "name" : "makeComplexResultError", "parameters" : [ + { + "label" : "_", + "name" : "message", + "type" : { + "string" : { + + } + } + }, + { + "label" : "_", + "name" : "code", + "type" : { + "int" : { + } + } + } ], "returnType" : { - "caseEnum" : { - "_0" : "Direction" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_processDirection", + "abiName" : "bjs_makeComplexResultLocation", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "processDirection", + "name" : "makeComplexResultLocation", "parameters" : [ { "label" : "_", - "name" : "input", + "name" : "lat", "type" : { - "caseEnum" : { - "_0" : "Direction" + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "lng", + "type" : { + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "name", + "type" : { + "string" : { + } } } ], "returnType" : { - "caseEnum" : { - "_0" : "Status" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_setTheme", + "abiName" : "bjs_makeComplexResultStatus", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "setTheme", + "name" : "makeComplexResultStatus", "parameters" : [ { "label" : "_", - "name" : "theme", + "name" : "active", "type" : { - "rawValueEnum" : { - "_0" : "Theme", - "_1" : "String" + "bool" : { + + } + } + }, + { + "label" : "_", + "name" : "code", + "type" : { + "int" : { + + } + } + }, + { + "label" : "_", + "name" : "message", + "type" : { + "string" : { + } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "Theme", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_getTheme", + "abiName" : "bjs_makeComplexResultCoordinates", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "getTheme", + "name" : "makeComplexResultCoordinates", "parameters" : [ + { + "label" : "_", + "name" : "x", + "type" : { + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "y", + "type" : { + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "z", + "type" : { + "double" : { + } + } + } ], "returnType" : { - "rawValueEnum" : { - "_0" : "Theme", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_setHttpStatus", + "abiName" : "bjs_makeComplexResultComprehensive", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "setHttpStatus", + "name" : "makeComplexResultComprehensive", "parameters" : [ { "label" : "_", - "name" : "status", + "name" : "flag1", "type" : { - "rawValueEnum" : { - "_0" : "HttpStatus", - "_1" : "Int" + "bool" : { + + } + } + }, + { + "label" : "_", + "name" : "flag2", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "_", + "name" : "count1", + "type" : { + "int" : { + + } + } + }, + { + "label" : "_", + "name" : "count2", + "type" : { + "int" : { + + } + } + }, + { + "label" : "_", + "name" : "value1", + "type" : { + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "value2", + "type" : { + "double" : { + + } + } + }, + { + "label" : "_", + "name" : "text1", + "type" : { + "string" : { + + } + } + }, + { + "label" : "_", + "name" : "text2", + "type" : { + "string" : { + + } + } + }, + { + "label" : "_", + "name" : "text3", + "type" : { + "string" : { + } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "HttpStatus", - "_1" : "Int" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_getHttpStatus", + "abiName" : "bjs_makeComplexResultInfo", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "getHttpStatus", + "name" : "makeComplexResultInfo", "parameters" : [ ], "returnType" : { - "rawValueEnum" : { - "_0" : "HttpStatus", - "_1" : "Int" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_processTheme", + "abiName" : "bjs_roundtripComplexResult", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "processTheme", + "name" : "roundtripComplexResult", "parameters" : [ { "label" : "_", - "name" : "theme", + "name" : "result", "type" : { - "rawValueEnum" : { - "_0" : "Theme", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "HttpStatus", - "_1" : "Int" + "associatedValueEnum" : { + "_0" : "ComplexResult" } } }, { - "abiName" : "bjs_setTSDirection", + "abiName" : "bjs_makeUtilitiesResultSuccess", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "setTSDirection", + "name" : "makeUtilitiesResultSuccess", "parameters" : [ { "label" : "_", - "name" : "direction", + "name" : "message", "type" : { - "caseEnum" : { - "_0" : "TSDirection" + "string" : { + } } } ], "returnType" : { - "caseEnum" : { - "_0" : "TSDirection" + "associatedValueEnum" : { + "_0" : "Utilities.Result" } } }, { - "abiName" : "bjs_getTSDirection", + "abiName" : "bjs_makeUtilitiesResultFailure", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "getTSDirection", + "name" : "makeUtilitiesResultFailure", "parameters" : [ + { + "label" : "_", + "name" : "error", + "type" : { + "string" : { - ], - "returnType" : { - "caseEnum" : { - "_0" : "TSDirection" - } - } - }, - { - "abiName" : "bjs_setTSTheme", - "effects" : { - "isAsync" : false, - "isThrows" : false - }, - "name" : "setTSTheme", - "parameters" : [ + } + } + }, { "label" : "_", - "name" : "theme", + "name" : "code", "type" : { - "rawValueEnum" : { - "_0" : "TSTheme", - "_1" : "String" + "int" : { + } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "TSTheme", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "Utilities.Result" } } }, { - "abiName" : "bjs_getTSTheme", + "abiName" : "bjs_makeUtilitiesResultStatus", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "getTSTheme", + "name" : "makeUtilitiesResultStatus", "parameters" : [ + { + "label" : "_", + "name" : "active", + "type" : { + "bool" : { - ], - "returnType" : { - "rawValueEnum" : { - "_0" : "TSTheme", - "_1" : "String" - } - } - }, - { - "abiName" : "bjs_echoNetworkingAPIMethod", - "effects" : { - "isAsync" : false, - "isThrows" : false - }, - "name" : "echoNetworkingAPIMethod", - "parameters" : [ + } + } + }, { "label" : "_", - "name" : "method", + "name" : "code", "type" : { - "caseEnum" : { - "_0" : "Networking.API.Method" + "int" : { + + } + } + }, + { + "label" : "_", + "name" : "message", + "type" : { + "string" : { + } } } ], "returnType" : { - "caseEnum" : { - "_0" : "Networking.API.Method" + "associatedValueEnum" : { + "_0" : "Utilities.Result" } } }, { - "abiName" : "bjs_echoConfigurationLogLevel", + "abiName" : "bjs_makeAPINetworkingResultSuccess", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "echoConfigurationLogLevel", + "name" : "makeAPINetworkingResultSuccess", "parameters" : [ { "label" : "_", - "name" : "level", + "name" : "message", "type" : { - "rawValueEnum" : { - "_0" : "Configuration.LogLevel", - "_1" : "String" + "string" : { + } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "Configuration.LogLevel", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "API.NetworkingResult" } } }, { - "abiName" : "bjs_echoConfigurationPort", + "abiName" : "bjs_makeAPINetworkingResultFailure", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "echoConfigurationPort", + "name" : "makeAPINetworkingResultFailure", "parameters" : [ { "label" : "_", - "name" : "port", + "name" : "error", "type" : { - "rawValueEnum" : { - "_0" : "Configuration.Port", - "_1" : "Int" + "string" : { + + } + } + }, + { + "label" : "_", + "name" : "code", + "type" : { + "int" : { + } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "Configuration.Port", - "_1" : "Int" + "associatedValueEnum" : { + "_0" : "API.NetworkingResult" } } }, { - "abiName" : "bjs_processConfigurationLogLevel", + "abiName" : "bjs_roundtripUtilitiesResult", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "processConfigurationLogLevel", + "name" : "roundtripUtilitiesResult", "parameters" : [ { "label" : "_", - "name" : "level", + "name" : "result", "type" : { - "rawValueEnum" : { - "_0" : "Configuration.LogLevel", - "_1" : "String" + "associatedValueEnum" : { + "_0" : "Utilities.Result" } } } ], "returnType" : { - "rawValueEnum" : { - "_0" : "Configuration.Port", - "_1" : "Int" + "associatedValueEnum" : { + "_0" : "Utilities.Result" } } }, { - "abiName" : "bjs_echoInternalSupportedMethod", + "abiName" : "bjs_roundtripAPINetworkingResult", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "echoInternalSupportedMethod", + "name" : "roundtripAPINetworkingResult", "parameters" : [ { "label" : "_", - "name" : "method", + "name" : "result", "type" : { - "caseEnum" : { - "_0" : "Internal.SupportedMethod" + "associatedValueEnum" : { + "_0" : "API.NetworkingResult" } } } ], "returnType" : { - "caseEnum" : { - "_0" : "Internal.SupportedMethod" + "associatedValueEnum" : { + "_0" : "API.NetworkingResult" } } }, diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 8094a465..fc7598e3 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -1,7 +1,7 @@ // @ts-check -import { - Direction, Status, Theme, HttpStatus, TSDirection, TSTheme +import { + Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult } from '../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ @@ -389,6 +389,89 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { const globalTestServer = new globalThis.Networking.APIV2.Internal.TestServer(); globalTestServer.call(globalThis.Networking.APIV2.Internal.SupportedMethod.Post); globalTestServer.release(); + + const s1 = { tag: APIResult.Tag.Success, param0: "Cześć 🙋‍♂️" }; + const f1 = { tag: APIResult.Tag.Failure, param0: 42 }; + const i1 = { tag: APIResult.Tag.Info }; + + assert.deepEqual(exports.echoAPIResult(s1), s1); + assert.deepEqual(exports.echoAPIResult(f1), f1); + assert.deepEqual(exports.echoAPIResult(i1), i1); + + + assert.deepEqual(exports.makeAPIResultSuccess("Test"), { tag: APIResult.Tag.Success, param0: "Test" }); + assert.deepEqual(exports.makeAPIResultSuccess("ok"), { tag: APIResult.Tag.Success, param0: "ok" }); + assert.deepEqual(exports.makeAPIResultFailure(123), { tag: APIResult.Tag.Failure, param0: 123 }); + assert.deepEqual(exports.makeAPIResultInfo(), { tag: APIResult.Tag.Info }); + + const bTrue = { tag: APIResult.Tag.Flag, param0: true }; + const bFalse = { tag: APIResult.Tag.Flag, param0: false }; + assert.deepEqual(exports.makeAPIResultFlag(true), bTrue); + assert.deepEqual(exports.makeAPIResultFlag(false), bFalse); + + const rVal = 3.25; + const r1 = { tag: APIResult.Tag.Rate, param0: rVal }; + assert.deepEqual(exports.echoAPIResult(r1), r1); + assert.deepEqual(exports.makeAPIResultRate(rVal), r1); + + const pVal = 3.141592653589793; + const p1 = { tag: APIResult.Tag.Precise, param0: pVal }; + assert.deepEqual(exports.echoAPIResult(p1), p1); + assert.deepEqual(exports.makeAPIResultPrecise(pVal), p1); + + const cs1 = { tag: ComplexResult.Tag.Success, param0: "All good!" }; + const ce1 = { tag: ComplexResult.Tag.Error, param0: "Network error", param1: 503 }; + const cl1 = { tag: ComplexResult.Tag.Location, param0: 37.7749, param1: -122.4194, param2: "San Francisco" }; + const cst1 = { tag: ComplexResult.Tag.Status, param0: true, param1: 200, param2: "OK" }; + const cc1 = { tag: ComplexResult.Tag.Coordinates, param0: 10.5, param1: 20.3, param2: 30.7 }; + const ccomp1 = { tag: ComplexResult.Tag.Comprehensive, param0: true, param1: false, param2: 42, param3: 100, param4: 3.14, param5: 2.718, param6: "Hello", param7: "World", param8: "Test" }; + const ci1 = { tag: ComplexResult.Tag.Info }; + + assert.deepEqual(exports.echoComplexResult(cs1), cs1); + assert.deepEqual(exports.echoComplexResult(ce1), ce1); + assert.deepEqual(exports.echoComplexResult(cl1), cl1); + assert.deepEqual(exports.echoComplexResult(cst1), cst1); + assert.deepEqual(exports.echoComplexResult(cc1), cc1); + assert.deepEqual(exports.echoComplexResult(ccomp1), ccomp1); + assert.deepEqual(exports.echoComplexResult(ci1), ci1); + + assert.deepEqual(exports.roundtripComplexResult(cs1), cs1); + assert.deepEqual(exports.roundtripComplexResult(ce1), ce1); + assert.deepEqual(exports.roundtripComplexResult(cl1), cl1); + assert.deepEqual(exports.roundtripComplexResult(cst1), cst1); + assert.deepEqual(exports.roundtripComplexResult(cc1), cc1); + assert.deepEqual(exports.roundtripComplexResult(ccomp1), ccomp1); + assert.deepEqual(exports.roundtripComplexResult(ci1), ci1); + + assert.deepEqual(exports.makeComplexResultSuccess("Great!"), { tag: ComplexResult.Tag.Success, param0: "Great!" }); + assert.deepEqual(exports.makeComplexResultError("Timeout", 408), { tag: ComplexResult.Tag.Error, param0: "Timeout", param1: 408 }); + assert.deepEqual(exports.makeComplexResultLocation(40.7128, -74.0060, "New York"), { tag: ComplexResult.Tag.Location, param0: 40.7128, param1: -74.0060, param2: "New York" }); + assert.deepEqual(exports.makeComplexResultStatus(false, 500, "Internal Server Error"), { tag: ComplexResult.Tag.Status, param0: false, param1: 500, param2: "Internal Server Error" }); + assert.deepEqual(exports.makeComplexResultCoordinates(1.1, 2.2, 3.3), { tag: ComplexResult.Tag.Coordinates, param0: 1.1, param1: 2.2, param2: 3.3 }); + assert.deepEqual(exports.makeComplexResultComprehensive(true, false, 10, 20, 1.5, 2.5, "First", "Second", "Third"), { tag: ComplexResult.Tag.Comprehensive, param0: true, param1: false, param2: 10, param3: 20, param4: 1.5, param5: 2.5, param6: "First", param7: "Second", param8: "Third" }); + assert.deepEqual(exports.makeComplexResultInfo(), { tag: ComplexResult.Tag.Info }); + + const urSuccess = { tag: globalThis.Utilities.Result.Tag.Success, param0: "Utility operation completed" }; + const urFailure = { tag: globalThis.Utilities.Result.Tag.Failure, param0: "Utility error occurred", param1: 500 }; + const urStatus = { tag: globalThis.Utilities.Result.Tag.Status, param0: true, param1: 200, param2: "Utility status OK" }; + + assert.deepEqual(exports.roundtripUtilitiesResult(urSuccess), urSuccess); + assert.deepEqual(exports.roundtripUtilitiesResult(urFailure), urFailure); + assert.deepEqual(exports.roundtripUtilitiesResult(urStatus), urStatus); + + assert.deepEqual(exports.makeUtilitiesResultSuccess("Test"), { tag: globalThis.Utilities.Result.Tag.Success, param0: "Test" }); + assert.deepEqual(exports.makeUtilitiesResultSuccess("ok"), { tag: globalThis.Utilities.Result.Tag.Success, param0: "ok" }); + assert.deepEqual(exports.makeUtilitiesResultFailure("Error", 123), { tag: globalThis.Utilities.Result.Tag.Failure, param0: "Error", param1: 123 }); + assert.deepEqual(exports.makeUtilitiesResultStatus(true, 200, "OK"), { tag: globalThis.Utilities.Result.Tag.Status, param0: true, param1: 200, param2: "OK" }); + + const nrSuccess = { tag: globalThis.API.NetworkingResult.Tag.Success, param0: "Network request successful" }; + const nrFailure = { tag: globalThis.API.NetworkingResult.Tag.Failure, param0: "Network timeout", param1: 408 }; + + assert.deepEqual(exports.roundtripAPINetworkingResult(nrSuccess), nrSuccess); + assert.deepEqual(exports.roundtripAPINetworkingResult(nrFailure), nrFailure); + + assert.deepEqual(exports.makeAPINetworkingResultSuccess("Connected"), { tag: globalThis.API.NetworkingResult.Tag.Success, param0: "Connected" }); + assert.deepEqual(exports.makeAPINetworkingResultFailure("Timeout", 408), { tag: globalThis.API.NetworkingResult.Tag.Failure, param0: "Timeout", param1: 408 }); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ @@ -487,4 +570,4 @@ function setupTestGlobals(global) { sym: Symbol("s"), bi: BigInt(3) }; -} +} \ No newline at end of file From 5fa3d23a198465f03bb1828e1402e7e897060760 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 12:06:53 +0900 Subject: [PATCH 2/9] Fix Benchmark build errors --- Benchmarks/Sources/Benchmarks.swift | 2 +- Benchmarks/run.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Benchmarks/Sources/Benchmarks.swift b/Benchmarks/Sources/Benchmarks.swift index 155acae1..f419e5a9 100644 --- a/Benchmarks/Sources/Benchmarks.swift +++ b/Benchmarks/Sources/Benchmarks.swift @@ -14,7 +14,7 @@ class Benchmark { body() return .undefined } - benchmarkRunner("\(title)/\(name)", jsBody) + try! benchmarkRunner("\(title)/\(name)", jsBody) } } diff --git a/Benchmarks/run.js b/Benchmarks/run.js index 2305373a..3ed8ca40 100644 --- a/Benchmarks/run.js +++ b/Benchmarks/run.js @@ -242,7 +242,7 @@ async function singleRun(results) { const options = await defaultNodeSetup({}) const { exports } = await instantiate({ ...options, - imports: { + getImports: () => ({ benchmarkHelperNoop: () => { }, benchmarkHelperNoopWithNumber: (n) => { }, benchmarkRunner: (name, body) => { @@ -255,7 +255,7 @@ async function singleRun(results) { } results[name].push(duration) } - } + }) }); exports.run(); } From 2cbf2d430771fca1562bdd96d3adb691261f8a12 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 12:10:10 +0900 Subject: [PATCH 3/9] Add filtering of benchmarks by name --- Benchmarks/README.md | 7 ++++++ Benchmarks/run.js | 55 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/Benchmarks/README.md b/Benchmarks/README.md index eeafc395..f7264b6b 100644 --- a/Benchmarks/README.md +++ b/Benchmarks/README.md @@ -27,4 +27,11 @@ node run.js --adaptive --output=stable-results.json # Run benchmarks and compare with previous results node run.js --baseline=previous-results.json + +# Run only a subset of benchmarks +# Substring match +node run.js --filter=Call +# Regex (with flags) +node run.js --filter=/^Property access\// +node run.js --filter=/string/i ``` diff --git a/Benchmarks/run.js b/Benchmarks/run.js index 3ed8ca40..b33e2091 100644 --- a/Benchmarks/run.js +++ b/Benchmarks/run.js @@ -21,6 +21,32 @@ function updateProgress(current, total, label = '', width) { process.stdout.write(`${label} [${bar}] ${current}/${total}`); } +/** + * Create a name filter function from CLI argument + * - Supports substring match (default) + * - Supports /regex/flags syntax + * @param {string|undefined} arg + * @returns {(name: string) => boolean} + */ +function createNameFilter(arg) { + if (!arg) { + return () => true; + } + if (arg.startsWith('/') && arg.lastIndexOf('/') > 0) { + const lastSlash = arg.lastIndexOf('/'); + const pattern = arg.slice(1, lastSlash); + const flags = arg.slice(lastSlash + 1); + try { + const re = new RegExp(pattern, flags); + return (name) => re.test(name); + } catch (e) { + console.error('Invalid regular expression for --filter:', e.message); + process.exit(1); + } + } + return (name) => name.includes(arg); +} + /** * Calculate coefficient of variation (relative standard deviation) * @param {Array} values - Array of measurement values @@ -238,7 +264,7 @@ function saveJsonResults(filePath, data) { * @param {Object} results - Results object to store benchmark data * @returns {Promise} */ -async function singleRun(results) { +async function singleRun(results, nameFilter) { const options = await defaultNodeSetup({}) const { exports } = await instantiate({ ...options, @@ -246,6 +272,9 @@ async function singleRun(results) { benchmarkHelperNoop: () => { }, benchmarkHelperNoopWithNumber: (n) => { }, benchmarkRunner: (name, body) => { + if (nameFilter && !nameFilter(name)) { + return; + } const startTime = performance.now(); body(); const endTime = performance.now(); @@ -266,7 +295,7 @@ async function singleRun(results) { * @param {Object} options - Adaptive sampling options * @returns {Promise} */ -async function runUntilStable(results, options, width) { +async function runUntilStable(results, options, width, nameFilter, filterArg) { const { minRuns = 5, maxRuns = 50, @@ -285,9 +314,14 @@ async function runUntilStable(results, options, width) { // Update progress with estimated completion updateProgress(runs, maxRuns, "Benchmark Progress:", width); - await singleRun(results); + await singleRun(results, nameFilter); runs++; + if (runs === 1 && Object.keys(results).length === 0) { + console.error(`\nNo benchmarks matched filter: ${filterArg}`); + process.exit(1); + } + // Check if we've reached minimum runs if (runs < minRuns) continue; @@ -349,6 +383,7 @@ Options: --min-runs=NUMBER Minimum runs for adaptive sampling (default: 5) --max-runs=NUMBER Maximum runs for adaptive sampling (default: 50) --target-cv=NUMBER Target coefficient of variation % (default: 5) + --filter=PATTERN Filter benchmarks by name (substring or /regex/flags) --help Show this help message `); } @@ -363,7 +398,8 @@ async function main() { adaptive: { type: 'boolean', default: false }, 'min-runs': { type: 'string', default: '5' }, 'max-runs': { type: 'string', default: '50' }, - 'target-cv': { type: 'string', default: '5' } + 'target-cv': { type: 'string', default: '5' }, + filter: { type: 'string' } } }); @@ -374,6 +410,8 @@ async function main() { const results = {}; const width = 30; + const filterArg = args.values.filter; + const nameFilter = createNameFilter(filterArg); if (args.values.adaptive) { // Adaptive sampling mode @@ -388,7 +426,7 @@ async function main() { console.log(`Results will be saved to: ${args.values.output}`); } - await runUntilStable(results, options, width); + await runUntilStable(results, options, width, nameFilter, filterArg); } else { // Fixed number of runs mode const runs = parseInt(args.values.runs, 10); @@ -410,7 +448,12 @@ async function main() { console.log("\nOverall Progress:"); for (let i = 0; i < runs; i++) { updateProgress(i, runs, "Benchmark Runs:", width); - await singleRun(results); + await singleRun(results, nameFilter); + if (i === 0 && Object.keys(results).length === 0) { + process.stdout.write("\n"); + console.error(`No benchmarks matched filter: ${filterArg}`); + process.exit(1); + } } updateProgress(runs, runs, "Benchmark Runs:", width); console.log("\n"); From 71cb086556609117bbbaa5beaca80aeaf55aef53 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 12:15:50 +0900 Subject: [PATCH 4/9] fixup! Fix Benchmark build errors --- Benchmarks/Sources/Benchmarks.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Benchmarks/Sources/Benchmarks.swift b/Benchmarks/Sources/Benchmarks.swift index f419e5a9..55b9f3eb 100644 --- a/Benchmarks/Sources/Benchmarks.swift +++ b/Benchmarks/Sources/Benchmarks.swift @@ -24,13 +24,13 @@ class Benchmark { call.testSuite("JavaScript function call through Wasm import") { for _ in 0..<20_000_000 { - benchmarkHelperNoop() + try! benchmarkHelperNoop() } } call.testSuite("JavaScript function call through Wasm import with int") { for _ in 0..<10_000_000 { - benchmarkHelperNoopWithNumber(42) + try! benchmarkHelperNoopWithNumber(42) } } From 545954a7d1ba7106757c87fbcfb1a78bf7d189be Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 12:16:09 +0900 Subject: [PATCH 5/9] Add enum roundtrip benchmark --- Benchmarks/Sources/Benchmarks.swift | 33 +++ .../Generated/BridgeJS.ExportSwift.swift | 175 +++++++++++++++ .../JavaScript/BridgeJS.ExportSwift.json | 210 ++++++++++++++++++ Benchmarks/run.js | 91 ++++++-- 4 files changed, 496 insertions(+), 13 deletions(-) diff --git a/Benchmarks/Sources/Benchmarks.swift b/Benchmarks/Sources/Benchmarks.swift index 55b9f3eb..54857d9c 100644 --- a/Benchmarks/Sources/Benchmarks.swift +++ b/Benchmarks/Sources/Benchmarks.swift @@ -18,6 +18,39 @@ class Benchmark { } } +@JS enum APIResult { + case success(String) + case failure(Int) + case flag(Bool) + case rate(Float) + case precise(Double) + case info +} + +@JS class EnumRoundtrip { + @JS init() {} + + @JS func take(_ value: APIResult) {} + @JS func makeSuccess() -> APIResult { + return .success("Hello, world") + } + @JS func makeFailure() -> APIResult { + return .failure(42) + } + @JS func makeFlag() -> APIResult { + return .flag(true) + } + @JS func makeRate() -> APIResult { + return .rate(0.5) + } + @JS func makePrecise() -> APIResult { + return .precise(0.5) + } + @JS func makeInfo() -> APIResult { + return .info + } +} + @JS func run() { let call = Benchmark("Call") diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index ee86def7..b330ebd2 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -6,6 +6,74 @@ @_spi(BridgeJS) import JavaScriptKit +private extension APIResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> APIResult { + let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in + _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) + initializedCount = Int(paramsLen) + } + return params.withUnsafeBytes { raw in + var reader = _BJSBinaryReader(raw: raw) + switch caseId { + case 0: + reader.readParamCount(expected: 1) + reader.expectTag(.string) + let param0 = reader.readString() + return .success(param0) + case 1: + reader.readParamCount(expected: 1) + reader.expectTag(.int32) + let param0 = Int(reader.readInt32()) + return .failure(param0) + case 2: + reader.readParamCount(expected: 1) + reader.expectTag(.bool) + let param0 = Int32(reader.readUInt8()) != 0 + return .flag(param0) + case 3: + reader.readParamCount(expected: 1) + reader.expectTag(.float32) + let param0 = reader.readFloat32() + return .rate(param0) + case 4: + reader.readParamCount(expected: 1) + reader.expectTag(.float64) + let param0 = reader.readFloat64() + return .precise(param0) + case 5: + return .info + default: + fatalError("Unknown APIResult case ID: \(caseId)") + } + } + } + + func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_return_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0): + _swift_js_return_tag(Int32(1)) + _swift_js_return_int(Int32(param0)) + case .flag(let param0): + _swift_js_return_tag(Int32(2)) + _swift_js_return_bool(param0 ? 1 : 0) + case .rate(let param0): + _swift_js_return_tag(Int32(3)) + _swift_js_return_f32(param0) + case .precise(let param0): + _swift_js_return_tag(Int32(4)) + _swift_js_return_f64(param0) + case .info: + _swift_js_return_tag(Int32(5)) + } + } +} + @_expose(wasm, "bjs_run") @_cdecl("bjs_run") public func _bjs_run() -> Void { @@ -14,4 +82,111 @@ public func _bjs_run() -> Void { #else fatalError("Only available on WebAssembly") #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_init") +@_cdecl("bjs_EnumRoundtrip_init") +public func _bjs_EnumRoundtrip_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = EnumRoundtrip() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_take") +@_cdecl("bjs_EnumRoundtrip_take") +public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32, valueParamsId: Int32, valueParamsLen: Int32) -> Void { + #if arch(wasm32) + EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId, valueParamsId, valueParamsLen)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makeSuccess") +@_cdecl("bjs_EnumRoundtrip_makeSuccess") +public func _bjs_EnumRoundtrip_makeSuccess(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makeSuccess() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makeFailure") +@_cdecl("bjs_EnumRoundtrip_makeFailure") +public func _bjs_EnumRoundtrip_makeFailure(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makeFailure() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makeFlag") +@_cdecl("bjs_EnumRoundtrip_makeFlag") +public func _bjs_EnumRoundtrip_makeFlag(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makeFlag() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makeRate") +@_cdecl("bjs_EnumRoundtrip_makeRate") +public func _bjs_EnumRoundtrip_makeRate(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makeRate() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makePrecise") +@_cdecl("bjs_EnumRoundtrip_makePrecise") +public func _bjs_EnumRoundtrip_makePrecise(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makePrecise() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_makeInfo") +@_cdecl("bjs_EnumRoundtrip_makeInfo") +public func _bjs_EnumRoundtrip_makeInfo(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = EnumRoundtrip.bridgeJSLiftParameter(_self).makeInfo() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EnumRoundtrip_deinit") +@_cdecl("bjs_EnumRoundtrip_deinit") +public func _bjs_EnumRoundtrip_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension EnumRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "Benchmarks", name: "bjs_EnumRoundtrip_wrap") + func _bjs_EnumRoundtrip_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_EnumRoundtrip_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_EnumRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())))) + } } \ No newline at end of file diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json index b00ec9ab..76807497 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -1,9 +1,219 @@ { "classes" : [ + { + "constructor" : { + "abiName" : "bjs_EnumRoundtrip_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_EnumRoundtrip_take", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "take", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makeSuccess", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makeSuccess", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makeFailure", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makeFailure", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makeFlag", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makeFlag", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makeRate", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makeRate", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makePrecise", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makePrecise", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + }, + { + "abiName" : "bjs_EnumRoundtrip_makeInfo", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "makeInfo", + "parameters" : [ + + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + ], + "name" : "EnumRoundtrip", + "properties" : [ + ], + "swiftCallName" : "EnumRoundtrip" + } ], "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "bool" : { + + } + } + } + ], + "name" : "flag" + }, + { + "associatedValues" : [ + { + "type" : { + "float" : { + } + } + } + ], + "name" : "rate" + }, + { + "associatedValues" : [ + { + "type" : { + "double" : { + + } + } + } + ], + "name" : "precise" + }, + { + "associatedValues" : [ + + ], + "name" : "info" + } + ], + "emitStyle" : "const", + "name" : "APIResult", + "swiftCallName" : "APIResult" + } ], "functions" : [ { diff --git a/Benchmarks/run.js b/Benchmarks/run.js index b33e2091..6bef1e4e 100644 --- a/Benchmarks/run.js +++ b/Benchmarks/run.js @@ -3,6 +3,7 @@ import { defaultNodeSetup } from "./.build/plugins/PackageToJS/outputs/Package/p import fs from 'fs'; import path from 'path'; import { parseArgs } from "util"; +import { APIResult } from "./.build/plugins/PackageToJS/outputs/Package/bridge-js.js"; /** * Update progress bar on the current line @@ -266,27 +267,91 @@ function saveJsonResults(filePath, data) { */ async function singleRun(results, nameFilter) { const options = await defaultNodeSetup({}) + const benchmarkRunner = (name, body) => { + if (nameFilter && !nameFilter(name)) { + return; + } + const startTime = performance.now(); + body(); + const endTime = performance.now(); + const duration = endTime - startTime; + if (!results[name]) { + results[name] = [] + } + results[name].push(duration) + } const { exports } = await instantiate({ ...options, getImports: () => ({ benchmarkHelperNoop: () => { }, benchmarkHelperNoopWithNumber: (n) => { }, - benchmarkRunner: (name, body) => { - if (nameFilter && !nameFilter(name)) { - return; - } - const startTime = performance.now(); - body(); - const endTime = performance.now(); - const duration = endTime - startTime; - if (!results[name]) { - results[name] = [] - } - results[name].push(duration) - } + benchmarkRunner: benchmarkRunner }) }); exports.run(); + + const enumRoundtrip = new exports.EnumRoundtrip(); + const iterations = 100_000; + benchmarkRunner("EnumRoundtrip/takeEnum success", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Success, param0: "Hello, world" }) + } + }) + benchmarkRunner("EnumRoundtrip/takeEnum failure", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Failure, param0: 42 }) + } + }) + benchmarkRunner("EnumRoundtrip/takeEnum flag", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Flag, param0: true }) + } + }) + benchmarkRunner("EnumRoundtrip/takeEnum rate", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Rate, param0: 0.5 }) + } + }) + benchmarkRunner("EnumRoundtrip/takeEnum precise", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Precise, param0: 0.5 }) + } + }) + benchmarkRunner("EnumRoundtrip/takeEnum info", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.take({ tag: APIResult.Tag.Info }) + } + }) + benchmarkRunner("EnumRoundtrip/makeSuccess", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makeSuccess() + } + }) + benchmarkRunner("EnumRoundtrip/makeFailure", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makeFailure() + } + }) + benchmarkRunner("EnumRoundtrip/makeFlag", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makeFlag() + } + }) + benchmarkRunner("EnumRoundtrip/makeRate", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makeRate() + } + }) + benchmarkRunner("EnumRoundtrip/makePrecise", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makePrecise() + } + }) + benchmarkRunner("EnumRoundtrip/makeInfo", () => { + for (let i = 0; i < iterations; i++) { + enumRoundtrip.makeInfo() + } + }) } /** From e8d7f8b60ab867a7f737e02586c91a38b345c161 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 12:38:22 +0900 Subject: [PATCH 6/9] Add StringRoundtrip benchmark --- Benchmarks/Sources/Benchmarks.swift | 8 +++ .../Generated/BridgeJS.ExportSwift.swift | 52 ++++++++++++++++ .../JavaScript/BridgeJS.ExportSwift.json | 59 +++++++++++++++++++ Benchmarks/run.js | 12 ++++ 4 files changed, 131 insertions(+) diff --git a/Benchmarks/Sources/Benchmarks.swift b/Benchmarks/Sources/Benchmarks.swift index 54857d9c..1e191861 100644 --- a/Benchmarks/Sources/Benchmarks.swift +++ b/Benchmarks/Sources/Benchmarks.swift @@ -51,6 +51,14 @@ class Benchmark { } } +@JS class StringRoundtrip { + @JS init() {} + @JS func take(_ value: String) {} + @JS func make() -> String { + return "Hello, world" + } +} + @JS func run() { let call = Benchmark("Call") diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index b330ebd2..87904bd9 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -189,4 +189,56 @@ extension EnumRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject { #endif return .object(JSObject(id: UInt32(bitPattern: _bjs_EnumRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())))) } +} + +@_expose(wasm, "bjs_StringRoundtrip_init") +@_cdecl("bjs_StringRoundtrip_init") +public func _bjs_StringRoundtrip_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = StringRoundtrip() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StringRoundtrip_take") +@_cdecl("bjs_StringRoundtrip_take") +public func _bjs_StringRoundtrip_take(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StringRoundtrip.bridgeJSLiftParameter(_self).take(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StringRoundtrip_make") +@_cdecl("bjs_StringRoundtrip_make") +public func _bjs_StringRoundtrip_make(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = StringRoundtrip.bridgeJSLiftParameter(_self).make() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StringRoundtrip_deinit") +@_cdecl("bjs_StringRoundtrip_deinit") +public func _bjs_StringRoundtrip_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension StringRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "Benchmarks", name: "bjs_StringRoundtrip_wrap") + func _bjs_StringRoundtrip_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_StringRoundtrip_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_StringRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())))) + } } \ No newline at end of file diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json index 76807497..f0d74391 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -138,6 +138,65 @@ ], "swiftCallName" : "EnumRoundtrip" + }, + { + "constructor" : { + "abiName" : "bjs_StringRoundtrip_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_StringRoundtrip_take", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "take", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_StringRoundtrip_make", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "make", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "StringRoundtrip", + "properties" : [ + + ], + "swiftCallName" : "StringRoundtrip" } ], "enums" : [ diff --git a/Benchmarks/run.js b/Benchmarks/run.js index 6bef1e4e..798611d1 100644 --- a/Benchmarks/run.js +++ b/Benchmarks/run.js @@ -352,6 +352,18 @@ async function singleRun(results, nameFilter) { enumRoundtrip.makeInfo() } }) + + const stringRoundtrip = new exports.StringRoundtrip(); + benchmarkRunner("StringRoundtrip/takeString", () => { + for (let i = 0; i < iterations; i++) { + stringRoundtrip.take("Hello, world") + } + }) + benchmarkRunner("StringRoundtrip/makeString", () => { + for (let i = 0; i < iterations; i++) { + stringRoundtrip.make() + } + }) } /** From 37cf4024043a334ba79598cb2376ee0b83a298e9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 14:37:19 +0900 Subject: [PATCH 7/9] WIP: Stack ABI --- .../Generated/BridgeJS.ExportSwift.swift | 64 +++++++------------ .../JavaScriptKit/BridgeJSInstrincics.swift | 18 ++++++ 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index 87904bd9..e64c42c3 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -7,44 +7,28 @@ @_spi(BridgeJS) import JavaScriptKit private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> APIResult { - let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in - _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped) - initializedCount = Int(paramsLen) - } - return params.withUnsafeBytes { raw in - var reader = _BJSBinaryReader(raw: raw) - switch caseId { - case 0: - reader.readParamCount(expected: 1) - reader.expectTag(.string) - let param0 = reader.readString() - return .success(param0) - case 1: - reader.readParamCount(expected: 1) - reader.expectTag(.int32) - let param0 = Int(reader.readInt32()) - return .failure(param0) - case 2: - reader.readParamCount(expected: 1) - reader.expectTag(.bool) - let param0 = Int32(reader.readUInt8()) != 0 - return .flag(param0) - case 3: - reader.readParamCount(expected: 1) - reader.expectTag(.float32) - let param0 = reader.readFloat32() - return .rate(param0) - case 4: - reader.readParamCount(expected: 1) - reader.expectTag(.float64) - let param0 = reader.readFloat64() - return .precise(param0) - case 5: - return .info - default: - fatalError("Unknown APIResult case ID: \(caseId)") - } + static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { + switch caseId { + case 0: + let bytes = _swift_js_pop_param_int32() + let count = _swift_js_pop_param_int32() + return .success(String.bridgeJSLiftParameter(bytes, count)) + case 1: + let param0 = _swift_js_pop_param_int32() + return .failure(Int(param0)) + case 2: + let param0 = _swift_js_pop_param_int32() + return .flag(Int32(param0) != 0) + case 3: + let param0 = _swift_js_pop_param_float64() + return .rate(Float(param0)) + case 4: + let param0 = _swift_js_pop_param_float64() + return .precise(param0) + case 5: + return .info + default: + fatalError("Unknown APIResult case ID: \(caseId)") } } @@ -97,9 +81,9 @@ public func _bjs_EnumRoundtrip_init() -> UnsafeMutableRawPointer { @_expose(wasm, "bjs_EnumRoundtrip_take") @_cdecl("bjs_EnumRoundtrip_take") -public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32, valueParamsId: Int32, valueParamsLen: Int32) -> Void { +public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32) -> Void { #if arch(wasm32) - EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId, valueParamsId, valueParamsLen)) + EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId)) #else fatalError("Only available on WebAssembly") #endif diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index 3107252d..ad4b973e 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -446,6 +446,24 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo } #endif +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_pop_param_int32") +@_spi(BridgeJS) public func _swift_js_pop_param_int32() -> Int32 +#else +@_spi(BridgeJS) public func _swift_js_pop_param_int32() -> Int32 { + _onlyAvailableOnWasm() +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "swift_js_pop_param_float64") +@_spi(BridgeJS) public func _swift_js_pop_param_float64() -> Float64 +#else +@_spi(BridgeJS) public func _swift_js_pop_param_float64() -> Float64 { + _onlyAvailableOnWasm() +} +#endif + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_tag") @_spi(BridgeJS) public func _swift_js_return_tag(_ tag: Int32) From d96a99c3faf2b8ba63304baf47d5c173a8e0c990 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 14:47:55 +0900 Subject: [PATCH 8/9] WIP: Flat ABI --- .../Generated/BridgeJS.ExportSwift.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index e64c42c3..b6685662 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -7,23 +7,23 @@ @_spi(BridgeJS) import JavaScriptKit private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ arg1: Int32, _ arg2: Int32, _ arg3: Float, _ arg4: Double) -> APIResult { switch caseId { case 0: - let bytes = _swift_js_pop_param_int32() - let count = _swift_js_pop_param_int32() + let bytes = arg1 + let count = arg2 return .success(String.bridgeJSLiftParameter(bytes, count)) case 1: - let param0 = _swift_js_pop_param_int32() + let param0 = arg1 return .failure(Int(param0)) case 2: - let param0 = _swift_js_pop_param_int32() + let param0 = arg1 return .flag(Int32(param0) != 0) case 3: - let param0 = _swift_js_pop_param_float64() + let param0 = arg1 return .rate(Float(param0)) case 4: - let param0 = _swift_js_pop_param_float64() + let param0 = arg4 return .precise(param0) case 5: return .info @@ -81,9 +81,9 @@ public func _bjs_EnumRoundtrip_init() -> UnsafeMutableRawPointer { @_expose(wasm, "bjs_EnumRoundtrip_take") @_cdecl("bjs_EnumRoundtrip_take") -public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32) -> Void { +public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32, valueArg1: Int32, valueArg2: Int32, valueArg3: Float, valueArg4: Double) -> Void { #if arch(wasm32) - EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId)) + EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId, valueArg1, valueArg2, valueArg3, valueArg4)) #else fatalError("Only available on WebAssembly") #endif From 41068e5a4b7cea75567e1f5e57da64bebc5d50f4 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 28 Aug 2025 14:57:22 +0900 Subject: [PATCH 9/9] WIP: Reduce flat args --- .../Sources/Generated/BridgeJS.ExportSwift.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index b6685662..c03aa099 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -7,7 +7,7 @@ @_spi(BridgeJS) import JavaScriptKit private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32, _ arg1: Int32, _ arg2: Int32, _ arg3: Float, _ arg4: Double) -> APIResult { + static func bridgeJSLiftParameter(_ caseId: Int32, _ arg1: Int32, _ arg2: Int32) -> APIResult { switch caseId { case 0: let bytes = arg1 @@ -23,8 +23,8 @@ private extension APIResult { let param0 = arg1 return .rate(Float(param0)) case 4: - let param0 = arg4 - return .precise(param0) + let param0 = arg1 + return .precise(Double(param0)) case 5: return .info default: @@ -81,9 +81,9 @@ public func _bjs_EnumRoundtrip_init() -> UnsafeMutableRawPointer { @_expose(wasm, "bjs_EnumRoundtrip_take") @_cdecl("bjs_EnumRoundtrip_take") -public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32, valueArg1: Int32, valueArg2: Int32, valueArg3: Float, valueArg4: Double) -> Void { +public func _bjs_EnumRoundtrip_take(_self: UnsafeMutableRawPointer, valueCaseId: Int32, valueArg1: Int32, valueArg2: Int32) -> Void { #if arch(wasm32) - EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId, valueArg1, valueArg2, valueArg3, valueArg4)) + EnumRoundtrip.bridgeJSLiftParameter(_self).take(_: APIResult.bridgeJSLiftParameter(valueCaseId, valueArg1, valueArg2)) #else fatalError("Only available on WebAssembly") #endif