From 55ee129bf239e0a688510d5aa5cd359ddb92c531 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 3 Sep 2025 09:30:48 +0200 Subject: [PATCH 1/5] BridgeJS: WIP: Swift Optional support Swift -> TS --- .../Sources/BridgeJSCore/ExportSwift.swift | 169 ++++- .../Sources/BridgeJSCore/ImportTS.swift | 71 +- .../BridgeJSCore/TypeDeclResolver.swift | 23 + .../Sources/BridgeJSLink/BridgeJSLink.swift | 115 +++- .../Sources/BridgeJSLink/JSGlueGen.swift | 179 +++++ .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 9 + .../TS2Skeleton/JavaScript/src/index.d.ts | 1 + .../Inputs/OptionalParameters.swift | 89 +++ .../Inputs/OptionalUnionTypes.d.ts | 15 + .../Inputs/StringParameter.swift | 1 + .../ArrayParameter.Import.js | 55 ++ .../BridgeJSLinkTests/Async.Export.js | 55 ++ .../BridgeJSLinkTests/Async.Import.js | 55 ++ .../EnumAssociatedValue.Export.js | 55 ++ .../BridgeJSLinkTests/EnumCase.Export.js | 55 ++ .../BridgeJSLinkTests/EnumNamespace.Export.js | 55 ++ .../BridgeJSLinkTests/EnumRawType.Export.js | 55 ++ .../BridgeJSLinkTests/Interface.Import.js | 55 ++ .../InvalidPropertyNames.Import.js | 55 ++ .../MultipleImportedTypes.Import.js | 55 ++ .../BridgeJSLinkTests/Namespaces.Export.js | 55 ++ .../OptionalParameters.Export.d.ts | 54 ++ .../OptionalParameters.Export.js | 585 +++++++++++++++++ .../OptionalUnionTypes.Import.d.ts | 31 + .../OptionalUnionTypes.Import.js | 249 +++++++ .../PrimitiveParameters.Export.js | 55 ++ .../PrimitiveParameters.Import.js | 55 ++ .../PrimitiveReturn.Export.js | 55 ++ .../PrimitiveReturn.Import.js | 55 ++ .../BridgeJSLinkTests/PropertyTypes.Export.js | 55 ++ .../StringParameter.Export.d.ts | 1 + .../StringParameter.Export.js | 64 ++ .../StringParameter.Import.js | 55 ++ .../BridgeJSLinkTests/StringReturn.Export.js | 55 ++ .../BridgeJSLinkTests/StringReturn.Import.js | 55 ++ .../BridgeJSLinkTests/SwiftClass.Export.js | 55 ++ .../TS2SkeletonLike.Import.js | 55 ++ .../BridgeJSLinkTests/Throws.Export.js | 55 ++ .../BridgeJSLinkTests/TypeAlias.Import.js | 55 ++ .../TypeScriptClass.Import.js | 55 ++ .../VoidParameterVoidReturn.Export.js | 55 ++ .../VoidParameterVoidReturn.Import.js | 55 ++ .../ExportSwiftTests/OptionalParameters.json | 612 ++++++++++++++++++ .../ExportSwiftTests/OptionalParameters.swift | 328 ++++++++++ .../ExportSwiftTests/StringParameter.json | 24 + .../ExportSwiftTests/StringParameter.swift | 11 + .../ImportTSTests/OptionalUnionTypes.swift | 189 ++++++ .../JavaScriptKit/BridgeJSInstrincics.swift | 352 +++++++++- .../BridgeJSRuntimeTests/ExportAPITests.swift | 57 ++ .../Generated/BridgeJS.ExportSwift.swift | 215 ++++++ .../JavaScript/BridgeJS.ExportSwift.json | 422 ++++++++++++ Tests/prelude.mjs | 72 ++- 52 files changed, 5311 insertions(+), 57 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index a8a50acf..4b725289 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -142,6 +142,14 @@ public class ExportSwift { hint: "Only primitive types and types defined in the same module are allowed" ) } + + private func diagnoseNestedOptional(node: some SyntaxProtocol, type: String) { + diagnose( + node: node, + message: "Nested optional types are not supported: \(type)", + hint: "Use a single optional like String? instead of String?? or Optional>" + ) + } override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { switch state { @@ -183,17 +191,32 @@ public class ExportSwift { var parameters: [Parameter] = [] for param in node.signature.parameterClause.parameters { - guard let type = self.parent.lookupType(for: param.type) else { + let resolvedType = self.parent.lookupType(for: param.type) + + if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { + diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription) + continue + } + + guard let type = resolvedType else { diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription) continue } + let name = param.secondName?.text ?? param.firstName.text let label = param.firstName.text parameters.append(Parameter(label: label, name: name, type: type)) } let returnType: BridgeType if let returnClause = node.signature.returnClause { - guard let type = self.parent.lookupType(for: returnClause.type) else { + let resolvedType = self.parent.lookupType(for: returnClause.type) + + if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { + diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription) + return nil + } + + guard let type = resolvedType else { diagnoseUnsupportedType(node: returnClause.type, type: returnClause.type.trimmedDescription) return nil } @@ -712,10 +735,53 @@ public class ExportSwift { } func lookupType(for type: TypeSyntax) -> BridgeType? { - if let primitive = BridgeType(swiftType: type.trimmedDescription) { - return primitive + // 1. Handle T? syntax (OptionalTypeSyntax) + if let optionalType = type.as(OptionalTypeSyntax.self) { + let wrappedType = optionalType.wrappedType + if let baseType = lookupType(for: wrappedType) { + return .optional(baseType) + } + } + + // 2. Handle Optional syntax (IdentifierTypeSyntax with generic arguments) + if let identifierType = type.as(IdentifierTypeSyntax.self), + identifierType.name.text == "Optional", + let genericArgs = identifierType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = genericArgs.first?.argument { + if let baseType = lookupType(for: argType) { + return .optional(baseType) + } + } + + // 3. Handle Swift.Optional syntax (MemberTypeSyntax) + if let memberType = type.as(MemberTypeSyntax.self), + let baseType = memberType.baseType.as(IdentifierTypeSyntax.self), + baseType.name.text == "Swift", + memberType.name.text == "Optional", + let genericArgs = memberType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = genericArgs.first?.argument { + if let wrappedType = lookupType(for: argType) { + return .optional(wrappedType) + } + } + + // 4. Handle type aliases - try to resolve through type declaration resolver first + if let aliasDecl = typeDeclResolver.resolveTypeAlias(type) { + // Recursively lookup the aliased type + if let resolvedType = lookupType(for: aliasDecl.initializer.value) { + return resolvedType + } } + // 5. Handle primitive types before falling back to other type declarations + let typeName = type.trimmedDescription + if let primitiveType = BridgeType.primitive(swiftType: typeName) { + return primitiveType + } + + // 6. Try to resolve other type declarations guard let typeDecl = typeDeclResolver.resolve(type) else { return nil } @@ -831,9 +897,19 @@ public class ExportSwift { } else { argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter } } + + // For optional types, use Optional syntax instead of WrappedType? + let typeNameForIntrinsic: String + switch param.type { + case .optional(let wrappedType): + typeNameForIntrinsic = "Optional<\(wrappedType.swiftType)>" + default: + typeNameForIntrinsic = param.type.swiftType + } + liftedParameterExprs.append( ExprSyntax( - "\(raw: param.type.swiftType).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" + "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" ) ) for (name, type) in zip(argumentsToLift, liftingInfo.parameters.map { $0.type }) { @@ -928,6 +1004,12 @@ public class ExportSwift { return } + // Handle optional types that use special storage functions and return Void + if case .optional(_) = returnType { + append("return ret.bridgeJSLowerReturn()") + return + } + append("return ret.bridgeJSLowerReturn()") } @@ -1085,6 +1167,8 @@ public class ExportSwift { return "\(paramName)Float.bridgeJSLiftParameter(_swift_js_pop_param_f32())" case .double: return "\(paramName)Double.bridgeJSLiftParameter(_swift_js_pop_param_f64())" + case .optional(_): + return "/* Optional associated value lifting not yet implemented */nil" // Placeholder default: return "\(paramName)Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())" } @@ -1121,6 +1205,8 @@ public class ExportSwift { bodyLines.append("_swift_js_push_f32(\(paramName))") case .double: bodyLines.append("_swift_js_push_f64(\(paramName))") + case .optional(_): + bodyLines.append("// Optional associated value lowering not yet implemented") // Placeholder default: bodyLines.append( "preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")" @@ -1331,24 +1417,55 @@ extension AttributeListSyntax { } extension BridgeType { + // This initializer is primarily for string-based type descriptions, + // especially for internal and raw type lookups. + // For full SwiftSyntax based parsing, `ExportSwift.lookupType(for:)` should be used. init?(swiftType: String) { + // Use SwiftSyntax to parse the string into a TypeSyntax for robust optional detection + let typeSyntax = TypeSyntax(stringLiteral: swiftType) + + // 1. Handle T? syntax (OptionalTypeSyntax) + if let optionalType = typeSyntax.as(OptionalTypeSyntax.self) { + let wrappedTypeString = optionalType.wrappedType.trimmedDescription + if let baseType = BridgeType(swiftType: wrappedTypeString) { + self = .optional(baseType) + return + } + } + + // 2. Handle Optional syntax (IdentifierTypeSyntax with generic arguments) + if let identifierType = typeSyntax.as(IdentifierTypeSyntax.self), + identifierType.name.text == "Optional", + let genericArgs = identifierType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = genericArgs.first?.argument { + let innerTypeString = argType.trimmedDescription + if let baseType = BridgeType(swiftType: innerTypeString) { + self = .optional(baseType) + return + } + } + + // Fallback to primitive type handling if not an optional + if let primitive = BridgeType.primitive(swiftType: swiftType) { + self = primitive + return + } + + return nil + } + + // Helper for direct primitive type mapping (used by init? and elsewhere) + fileprivate static func primitive(swiftType: String) -> BridgeType? { switch swiftType { - case "Int": - self = .int - case "Float": - self = .float - case "Double": - self = .double - case "String": - self = .string - case "Bool": - self = .bool - case "Void": - self = .void - case "JSObject": - self = .jsObject(nil) - default: - return nil + case "Int": return .int + case "Float": return .float + case "Double": return .double + case "String": return .string + case "Bool": return .bool + case "Void": return .void + case "JSObject": return .jsObject(nil) + default: return nil } } } @@ -1377,6 +1494,7 @@ extension BridgeType { case .jsObject(let name?): return name case .swiftHeapObject(let name): return name case .void: return "Void" + case .optional(let wrappedType): return "\(wrappedType.swiftType)?" case .caseEnum(let name): return name case .rawValueEnum(let name, _): return name case .associatedValueEnum(let name): return name @@ -1400,6 +1518,7 @@ extension BridgeType { ("caseId", .i32) ]) } + func liftParameterInfo() throws -> LiftingIntrinsicInfo { switch self { @@ -1411,6 +1530,11 @@ extension BridgeType { case .jsObject: return .jsObject case .swiftHeapObject: return .swiftHeapObject case .void: return .void + case .optional(let wrappedType): + // Optional parameters include optionality flag plus wrapped type parameters + var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] + optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters) + return LiftingIntrinsicInfo(parameters: optionalParams) case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): switch rawType { @@ -1458,6 +1582,9 @@ extension BridgeType { case .jsObject: return .jsObject case .swiftHeapObject: return .swiftHeapObject case .void: return .void + case .optional(_): + // Optional return values use special storage functions and return void + return LoweringIntrinsicInfo(returnType: nil) case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): switch rawType { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 9fc8a62d..2fd27fce 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -72,18 +72,51 @@ public struct ImportTS { func lowerParameter(param: Parameter) throws { let loweringInfo = try param.type.loweringParameterInfo() - assert( - loweringInfo.loweredParameters.count == 1, - "For now, we require a single parameter to be lowered to a single Wasm core type" - ) - let (_, type) = loweringInfo.loweredParameters[0] - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") + + // Handle void parameters (empty loweredParameters array) + if loweringInfo.loweredParameters.isEmpty { + // Void parameters don't generate any ABI parameters + return + } else if loweringInfo.loweredParameters.count == 1 { + // Simple case: single parameter + let (_, type) = loweringInfo.loweredParameters[0] + abiParameterForwardings.append( + LabeledExprSyntax( + label: param.label, + expression: ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") + ) ) - ) - abiParameterSignatures.append((param.name, type)) + abiParameterSignatures.append((param.name, type)) + } else { + // Complex case: multiple parameters (e.g., optionals with isSome flag + wrapped parameters) + // For optional types, use Optional syntax for the lowering call + let typeNameForLowering: String + switch param.type { + case .optional(let wrappedType): + typeNameForLowering = "Optional<\(wrappedType.swiftType)>" + default: + typeNameForLowering = param.type.swiftType + } + + let paramNames = loweringInfo.loweredParameters.enumerated().map { index, paramInfo in + if index == 0 { + return param.name + paramInfo.name.capitalizedFirstLetter + } else { + return param.name + paramInfo.name.capitalizedFirstLetter + } + } + + abiParameterForwardings.append( + LabeledExprSyntax( + label: param.label, + expression: ExprSyntax("\(raw: typeNameForLowering).bridgeJSLowerParameter(\(raw: param.name))") + ) + ) + + for (paramName, (_, type)) in zip(paramNames, loweringInfo.loweredParameters) { + abiParameterSignatures.append((paramName, type)) + } + } } func call(returnType: BridgeType) { @@ -103,6 +136,13 @@ public struct ImportTS { if returnType == .void { return } + + // Handle optional return values that use special storage functions + if case .optional(_) = returnType { + body.append("return \(raw: returnType.swiftType).bridgeJSLiftReturn()") + return + } + body.append("return \(raw: returnType.swiftType).bridgeJSLiftReturn(ret)") } @@ -433,6 +473,12 @@ extension BridgeType { case .string: return .string case .jsObject: return .jsObject case .void: return .void + case .optional(let wrappedType): + // Optional parameters include optionality flag plus wrapped type parameters + let wrappedInfo = try wrappedType.loweringParameterInfo() + var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] + optionalParams.append(contentsOf: wrappedInfo.loweredParameters) + return LoweringParameterInfo(loweredParameters: optionalParams) case .swiftHeapObject: throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures") case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum: @@ -461,6 +507,9 @@ extension BridgeType { case .string: return .string case .jsObject: return .jsObject case .void: return .void + case .optional(_): + // Optional return values use special storage functions and return void + return LiftingReturnInfo(valueToLift: nil) case .swiftHeapObject: throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures") case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum: diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift index 25200e13..282837c6 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift @@ -8,6 +8,7 @@ class TypeDeclResolver { /// `Outer.Inner` type declaration is represented as ["Outer", "Inner"] typealias QualifiedName = [String] private var typeDeclByQualifiedName: [QualifiedName: TypeDecl] = [:] + private var typeAliasByQualifiedName: [QualifiedName: TypeAliasDeclSyntax] = [:] enum Error: Swift.Error { case typeNotFound(QualifiedName) @@ -62,6 +63,13 @@ class TypeDeclResolver { override func visitPost(_ node: EnumDeclSyntax) { visitPostNominalDecl() } + + override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { + let name = node.name.text + let qualifiedName = scope.map(\.name.text) + [name] + resolver.typeAliasByQualifiedName[qualifiedName] = node + return .skipChildren + } } /// Collects type declarations from a parsed Swift source file @@ -131,6 +139,21 @@ class TypeDeclResolver { } return nil } + + /// Resolves a type usage node to a type alias declaration + /// + /// - Parameter type: The SwiftSyntax node representing a type appearance in source code. + /// - Returns: The type alias declaration if found, otherwise nil. + func resolveTypeAlias(_ type: TypeSyntax) -> TypeAliasDeclSyntax? { + if let id = type.as(IdentifierTypeSyntax.self) { + let qualifiedName = tryQualify(type: id) + return typeAliasByQualifiedName[qualifiedName] + } + if let components = qualifiedComponents(from: type) { + return typeAliasByQualifiedName[components] + } + return nil + } private func qualifiedComponents(from type: TypeSyntax) -> QualifiedName? { if let m = type.as(MemberTypeSyntax.self) { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 7de0ec53..31717fed 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -189,6 +189,11 @@ struct BridgeJSLink { "let \(JSGlueVariableScope.reservedStorageToReturnString);", "let \(JSGlueVariableScope.reservedStorageToReturnBytes);", "let \(JSGlueVariableScope.reservedStorageToReturnException);", + "let \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);", + "let \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);", + "let \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);", + "let \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);", + "let \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);", "let \(JSGlueVariableScope.reservedTmpRetTag);", "let \(JSGlueVariableScope.reservedTmpRetStrings) = [];", "let \(JSGlueVariableScope.reservedTmpRetInts) = [];", @@ -233,7 +238,6 @@ struct BridgeJSLink { ) } printer.write("}") - printer.write("bjs[\"swift_js_init_memory\"] = function(sourceId, bytesPtr) {") printer.indent { printer.write( @@ -245,7 +249,6 @@ struct BridgeJSLink { printer.write("bytes.set(source);") } printer.write("}") - printer.write("bjs[\"swift_js_make_js_string\"] = function(ptr, len) {") printer.indent { printer.write( @@ -256,7 +259,6 @@ struct BridgeJSLink { ) } printer.write("}") - printer.write("bjs[\"swift_js_init_memory_with_result\"] = function(ptr, len) {") printer.indent { printer.write( @@ -266,7 +268,6 @@ struct BridgeJSLink { printer.write("\(JSGlueVariableScope.reservedStorageToReturnBytes) = undefined;") } printer.write("}") - printer.write("bjs[\"swift_js_throw\"] = function(id) {") printer.indent { printer.write( @@ -274,7 +275,6 @@ struct BridgeJSLink { ) } printer.write("}") - printer.write("bjs[\"swift_js_retain\"] = function(id) {") printer.indent { printer.write( @@ -282,37 +282,31 @@ struct BridgeJSLink { ) } printer.write("}") - printer.write("bjs[\"swift_js_release\"] = function(id) {") printer.indent { printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(id);") } printer.write("}") - printer.write("bjs[\"swift_js_push_tag\"] = function(tag) {") printer.indent { printer.write("\(JSGlueVariableScope.reservedTmpRetTag) = tag;") } printer.write("}") - printer.write("bjs[\"swift_js_push_int\"] = function(v) {") printer.indent { printer.write("\(JSGlueVariableScope.reservedTmpRetInts).push(v | 0);") } printer.write("}") - printer.write("bjs[\"swift_js_push_f32\"] = function(v) {") printer.indent { printer.write("\(JSGlueVariableScope.reservedTmpRetF32s).push(Math.fround(v));") } printer.write("}") - printer.write("bjs[\"swift_js_push_f64\"] = function(v) {") printer.indent { printer.write("\(JSGlueVariableScope.reservedTmpRetF64s).push(v);") } printer.write("}") - printer.write("bjs[\"swift_js_push_string\"] = function(ptr, len) {") printer.indent { printer.write( @@ -322,24 +316,115 @@ struct BridgeJSLink { printer.write("\(JSGlueVariableScope.reservedTmpRetStrings).push(value);") } printer.write("}") - printer.write("bjs[\"swift_js_pop_param_int32\"] = function() {") printer.indent { printer.write("return \(JSGlueVariableScope.reservedTmpParamInts).pop();") } printer.write("}") - printer.write("bjs[\"swift_js_pop_param_f32\"] = function() {") printer.indent { printer.write("return \(JSGlueVariableScope.reservedTmpParamF32s).pop();") } printer.write("}") - printer.write("bjs[\"swift_js_pop_param_f64\"] = function() {") printer.indent { printer.write("return \(JSGlueVariableScope.reservedTmpParamF64s).pop();") } printer.write("}") + printer.write("bjs[\"swift_js_return_optional_bool\"] = function(isSome, value) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = value !== 0;") + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_int\"] = function(isSome, value) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = value | 0;") + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_float\"] = function(isSome, value) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = Math.fround(value);") + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_double\"] = function(isSome, value) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = value;") + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_string\"] = function(isSome, ptr, len) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = null;") + } + printer.write("} else {") + printer.indent { + printer.write(lines: [ + "const bytes = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, ptr, len);", + "\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);" + ]) + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_object\"] = function(isSome, objectId) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedSwift).memory.getObject(objectId);") + } + printer.write("}") + } + printer.write("}") + printer.write("bjs[\"swift_js_return_optional_heap_object\"] = function(isSome, pointer) {") + printer.indent { + printer.write("if (isSome === 0) {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = pointer;") + } + printer.write("}") + } + printer.write("}") } } return printer @@ -1676,6 +1761,8 @@ extension BridgeType { return name ?? "any" case .swiftHeapObject(let name): return name + case .optional(let wrappedType): + return "\(wrappedType.tsType) | null" case .caseEnum(let name): return name case .rawValueEnum(let name, _): diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 36977aae..932ee496 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -13,6 +13,11 @@ final class JSGlueVariableScope { static let reservedStorageToReturnString = "tmpRetString" static let reservedStorageToReturnBytes = "tmpRetBytes" static let reservedStorageToReturnException = "tmpRetException" + static let reservedStorageToReturnOptionalBool = "tmpRetOptionalBool" + static let reservedStorageToReturnOptionalInt = "tmpRetOptionalInt" + static let reservedStorageToReturnOptionalFloat = "tmpRetOptionalFloat" + static let reservedStorageToReturnOptionalDouble = "tmpRetOptionalDouble" + static let reservedStorageToReturnOptionalHeapObject = "tmpRetOptionalHeapObject" static let reservedTextEncoder = "textEncoder" static let reservedTextDecoder = "textDecoder" static let reservedTmpRetTag = "tmpRetTag" @@ -30,6 +35,11 @@ final class JSGlueVariableScope { reservedStorageToReturnString, reservedStorageToReturnBytes, reservedStorageToReturnException, + reservedStorageToReturnOptionalBool, + reservedStorageToReturnOptionalInt, + reservedStorageToReturnOptionalFloat, + reservedStorageToReturnOptionalDouble, + reservedStorageToReturnOptionalHeapObject, reservedTextEncoder, reservedTextDecoder, reservedTmpRetTag, @@ -250,6 +260,72 @@ struct IntrinsicJSFragment: Sendable { case .swiftHeapObject: return .swiftHeapObjectLowerParameter case .void: return .void + case .optional(let wrappedType): + // Pre-compute the wrapped type fragment outside the closure + let wrappedFragment = try lowerParameter(type: wrappedType) + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanupCode in + let value = arguments[0] + let isNullVar = scope.variable("isNull") + let isSomeVar = scope.variable("isSome") + let resultVars = scope.variable("results") + + printer.write("const \(isNullVar) = (\(value) === null || \(value) === undefined);") + printer.write("const \(isSomeVar) = \(isNullVar) ? 0 : 1;") + printer.write("let \(resultVars) = [\(isSomeVar)];") + + // Handle different wrapped types with proper variable scoping + switch wrappedType { + case .string: + // Pre-declare variables for string handling + let bytesVar = scope.variable("\(value)Bytes") + let idVar = scope.variable("\(value)Id") + printer.write("let \(idVar);") + + printer.write("if (!\(isNullVar)) {") + printer.indent { + printer.write("const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));") + printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") + printer.write("\(resultVars).push(\(idVar));") + printer.write("\(resultVars).push(\(bytesVar).length);") + } + printer.write("} else {") + printer.indent { + printer.write("\(resultVars).push(0);") // dummy bytes ID + printer.write("\(resultVars).push(0);") // dummy length + } + printer.write("}") + + // Add conditional cleanup + cleanupCode.write("if (\(idVar) !== undefined) {") + cleanupCode.write(" \(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") + cleanupCode.write("}") + + default: + // For other non-string types, use the original delegation approach + printer.write("if (!\(isNullVar)) {") + printer.indent { + let wrappedResults = wrappedFragment.printCode([value], scope, printer, cleanupCode) + for result in wrappedResults { + printer.write("\(resultVars).push(\(result));") + } + } + printer.write("} else {") + printer.indent { + switch wrappedType { + case .int, .bool, .float, .double: + printer.write("\(resultVars).push(0);") + default: + printer.write("\(resultVars).push(0);") + } + } + printer.write("}") + } + + return ["...\(resultVars)"] + } + ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -273,6 +349,43 @@ struct IntrinsicJSFragment: Sendable { case .jsObject: return .jsObjectLiftReturn case .swiftHeapObject(let name): return .swiftHeapObjectLiftReturn(name) case .void: return .void + case .optional(let wrappedType): + // For ExportSwift, optional returns use type-specific storage and return void + return IntrinsicJSFragment( + parameters: [], + printCode: { arguments, scope, printer, cleanupCode in + let resultVar = scope.variable("optResult") + + switch wrappedType { + case .bool: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") + case .int: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") + case .float: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") + case .double: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") + case .string: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") + case .swiftHeapObject(let className): + let pointerVar = scope.variable("pointer") + printer.write("const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;") + printer.write("const \(resultVar) = \(pointerVar) === null ? null : \(className).__construct(\(pointerVar));") + default: + // Fallback for unsupported optional types + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") + } + + return [resultVar] + } + ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -308,6 +421,36 @@ struct IntrinsicJSFragment: Sendable { throw BridgeJSLinkError( message: "Void can't appear in parameters of imported JS functions" ) + case .optional(let wrappedType): + // Pre-compute the wrapped type fragment outside the closure + let wrappedFragment = try liftParameter(type: wrappedType) + return IntrinsicJSFragment( + parameters: ["isSome"], + printCode: { arguments, scope, printer, cleanupCode in + let isSomeParam = arguments[0] + let resultVar = scope.variable("optResult") + + printer.write("let \(resultVar);") + printer.write("if (\(isSomeParam) === 0) {") + printer.indent { + printer.write("\(resultVar) = null;") + } + printer.write("} else {") + printer.indent { + // For non-null case, lift the wrapped type parameters + let liftedResults = wrappedFragment.printCode([], scope, printer, cleanupCode) + + if let firstResult = liftedResults.first { + printer.write("\(resultVar) = \(firstResult);") + } else { + printer.write("\(resultVar) = undefined;") + } + } + printer.write("}") + + return [resultVar] + } + ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -340,6 +483,42 @@ struct IntrinsicJSFragment: Sendable { message: "Swift heap objects are not supported to be returned from imported JS functions" ) case .void: return .void + case .optional(let wrappedType): + // Pre-compute the wrapped type fragment outside the closure + let wrappedFragment = try lowerReturn(type: wrappedType) + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanupCode in + let value = arguments[0] + let isNullVar = scope.variable("isNull") + let isSomeVar = scope.variable("isSome") + let resultVars = scope.variable("results") + + printer.write("const \(isNullVar) = (\(value) === null || \(value) === undefined);") + printer.write("const \(isSomeVar) = \(isNullVar) ? 0 : 1;") + printer.write("let \(resultVars) = [\(isSomeVar)];") + + printer.write("if (!\(isNullVar)) {") + printer.indent { + // For non-null values, get the lowered wrapped type values + let wrappedResults = wrappedFragment.printCode([value], scope, printer, cleanupCode) + for result in wrappedResults { + printer.write("\(resultVars).push(\(result));") + } + } + printer.write("} else {") + printer.indent { + // For null values, push dummy values based on wrapped type + let dummyResults = wrappedFragment.printCode(["null"], scope, printer, cleanupCode) + for result in dummyResults { + printer.write("\(resultVars).push(\(result));") + } + } + printer.write("}") + + return ["\(resultVars).slice()"] + } + ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index a0d4bd9f..0eccf2dc 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -4,6 +4,7 @@ public enum BridgeType: Codable, Equatable, Sendable { case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void + indirect case optional(BridgeType) case caseEnum(String) case rawValueEnum(String, SwiftEnumRawType) case associatedValueEnum(String) @@ -336,6 +337,8 @@ extension BridgeType { case .swiftHeapObject: // UnsafeMutableRawPointer is returned as an i32 pointer return .pointer + case .optional(_): + return nil case .caseEnum: return .i32 case .rawValueEnum(_, let rawType): @@ -346,4 +349,10 @@ extension BridgeType { return nil } } + + /// Returns true if this type is optional + public var isOptional: Bool { + if case .optional = self { return true } + return false + } } diff --git a/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts b/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts index b53e2420..269fc711 100644 --- a/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts +++ b/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts @@ -6,6 +6,7 @@ export type BridgeType = | { "bool": {} } | { "jsObject": { "_0": string } | {} } | { "void": {} } + | { "optional": { "_0": BridgeType } } export type Parameter = { name: string; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift new file mode 100644 index 00000000..31bb648b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift @@ -0,0 +1,89 @@ +@JS class Greeter { + @JS var name: String? + + @JS init(name?: String) { + self.name = name + } + @JS func greet() -> String { + return "Hello, " + (self.name ?? "stranger") + "!" + } + + @JS func changeName(name: String?) { + self.name = name + } +} + +@JS +func roundTripOptionalClass(value: Greeter?) -> Greeter? { + return value +} + +@JS +class OptionalPropertyHolder { + @JS var optionalName: String? = nil + @JS var optionalAge: Int? = nil + @JS var optionalGreeter: Greeter? = nil + + @JS init() {} +} + +@JS func testOptionalPropertyRoundtrip(_ holder: OptionalPropertyHolder?) -> OptionalPropertyHolder? + +@JS +func roundTripString(name: String?) -> String? { + return name +} + +@JS +func roundTripInt(value: Int?) -> Int? { + return value +} + +@JS +func roundTripBool(flag: Bool?) -> Bool? { + return flag +} + +@JS +func roundTripFloat(number: Float?) -> Float? { + return number +} + +@JS +func roundTripDouble(precision: Double?) -> Double? { + return precision +} + +@JS func roundTripSyntax(name: Optional) -> Optional { + return name +} + +@JS func roundTripMixSyntax(name: String?) -> Optional { + return name +} + +@JS func roundTripSwiftSyntax(name: Swift.Optional) -> Swift.Optional { + return name +} + +@JS func roundTripMixedSwiftSyntax(name: String?) -> Swift.Optional { + return name +} + +@JS func roundTripWithSpaces(value: Optional < Double >) -> Optional < Double > { + return value +} + +typealias OptionalAge = Int? + +@JS func roundTripAlias(age: OptionalAge) -> OptionalAge { + return age +} + +@JS +func testMixedOptionals(firstName: String?, lastName: String?age: Int?, active: Bool) -> String? { + if let firstName = firstName, let lastName = lastName, let age = age { + return "\(firstName)-\(lastName)-\(age)-\(active)" + } + return nil +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts new file mode 100644 index 00000000..d2855a1a --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts @@ -0,0 +1,15 @@ +export function testOptionalNumber(value: number | null): void; +export function testOptionalString(value: string | undefined): void; +export function testOptionalBool(value: boolean | null | undefined): void; +export function testOptionalReturn(): string | null; +export function testOptionalNumberReturn(): number | undefined; +export function testMixedOptionals(required: string, optional: number | null): boolean | undefined; + +export class TestClass { + optionalProperty: string | null; + + constructor(param: number | undefined); + + methodWithOptional(value: boolean | null): void; + methodReturningOptional(): string | undefined; +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift index e6763d4c..c735c56f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift @@ -1 +1,2 @@ @JS func checkString(a: String) {} +@JS func roundtripString(a: String) -> String { return a } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js index 1534a0fb..7295699a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkArray"] = function bjs_checkArray(a) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js index bdc30973..92f10f94 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js index d1d11903..74fb30c5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_asyncReturnVoid"] = function bjs_asyncReturnVoid() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index 280f0c0a..eca4f3c7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -361,6 +361,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -432,6 +437,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js index 80fb2399..fadc678a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js @@ -37,6 +37,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -107,6 +112,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js index c5b50719..0aff338b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js @@ -59,6 +59,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -129,6 +134,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js index 6aa897c3..a1b5dcf3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js @@ -88,6 +88,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -158,6 +163,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js index a897ccee..cead874b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_returnAnimatable"] = function bjs_returnAnimatable() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js index 670a27d7..f469072f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createArrayBuffer"] = function bjs_createArrayBuffer() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js index 1e09d4e3..116933d7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MultipleImportedTypes.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createDatabaseConnection"] = function bjs_createDatabaseConnection(config) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js index 23fb7990..4631fb24 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts new file mode 100644 index 00000000..ab194d83 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts @@ -0,0 +1,54 @@ +// 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`. + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface Greeter extends SwiftHeapObject { + greet(): string; + changeName(name: string | null): void; + name: string | null; +} +export interface OptionalPropertyHolder extends SwiftHeapObject { + optionalName: string | null; + optionalAge: number | null; + optionalGreeter: Greeter | null; +} +export type Exports = { + Greeter: { + new(name: string): Greeter; + } + OptionalPropertyHolder: { + new(): OptionalPropertyHolder; + } + roundTripOptionalClass(value: Greeter | null): Greeter | null; + testOptionalPropertyRoundtrip(holder: OptionalPropertyHolder | null): OptionalPropertyHolder | null; + roundTripString(name: string | null): string | null; + roundTripInt(value: number | null): number | null; + roundTripBool(flag: boolean | null): boolean | null; + roundTripFloat(number: number | null): number | null; + roundTripDouble(precision: number | null): number | null; + roundTripSyntax(name: string | null): string | null; + roundTripMixSyntax(name: string | null): string | null; + roundTripSwiftSyntax(name: string | null): string | null; + roundTripMixedSwiftSyntax(name: string | null): string | null; + roundTripWithSpaces(value: number | null): number | null; + roundTripAlias(age: number | null): number | null; + testMixedOptionals(firstName: string | null, lastName: string | null): string | null; +} +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/OptionalParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js new file mode 100644 index 00000000..5a9508af --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js @@ -0,0 +1,585 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + + 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); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_Greeter_wrap"] = function(pointer) { + const obj = Greeter.__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_OptionalPropertyHolder_wrap"] = function(pointer) { + const obj = OptionalPropertyHolder.__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype) { + const obj = Object.create(prototype); + obj.pointer = pointer; + obj.hasReleased = false; + obj.deinit = deinit; + obj.registry = new FinalizationRegistry((pointer) => { + deinit(pointer); + }); + obj.registry.register(this, obj.pointer); + return obj; + } + + release() { + this.registry.unregister(this); + this.deinit(this.pointer); + } + } + class Greeter extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype); + } + + constructor(name) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length); + swift.memory.release(nameId); + return Greeter.__construct(ret); + } + greet() { + instance.exports.bjs_Greeter_greet(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + changeName(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_Greeter_changeName(this.pointer, ...results); + if (nameId !== undefined) { + swift.memory.release(nameId); + } + } + get name() { + instance.exports.bjs_Greeter_name_get(this.pointer); + const optResult = tmpRetString; + tmpRetString = undefined; + return optResult; + } + set name(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let valueId; + if (!isNull) { + const valueBytes = textEncoder.encode(value); + valueId = swift.memory.retain(valueBytes); + results.push(valueId); + results.push(valueBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_Greeter_name_set(this.pointer, ...results); + if (valueId !== undefined) { + swift.memory.release(valueId); + } + } + } + class OptionalPropertyHolder extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_OptionalPropertyHolder_deinit, OptionalPropertyHolder.prototype); + } + + constructor() { + const ret = instance.exports.bjs_OptionalPropertyHolder_init(); + return OptionalPropertyHolder.__construct(ret); + } + get optionalName() { + instance.exports.bjs_OptionalPropertyHolder_optionalName_get(this.pointer); + const optResult = tmpRetString; + tmpRetString = undefined; + return optResult; + } + set optionalName(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let valueId; + if (!isNull) { + const valueBytes = textEncoder.encode(value); + valueId = swift.memory.retain(valueBytes); + results.push(valueId); + results.push(valueBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_OptionalPropertyHolder_optionalName_set(this.pointer, ...results); + if (valueId !== undefined) { + swift.memory.release(valueId); + } + } + get optionalAge() { + instance.exports.bjs_OptionalPropertyHolder_optionalAge_get(this.pointer); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + } + set optionalAge(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(value); + } else { + results.push(0); + } + instance.exports.bjs_OptionalPropertyHolder_optionalAge_set(this.pointer, ...results); + } + get optionalGreeter() { + instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_get(this.pointer); + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + const optResult = pointer === null ? null : Greeter.__construct(pointer); + return optResult; + } + set optionalGreeter(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(value.pointer); + } else { + results.push(0); + } + instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_set(this.pointer, ...results); + } + } + return { + Greeter, + OptionalPropertyHolder, + roundTripOptionalClass: function bjs_roundTripOptionalClass(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(value.pointer); + } else { + results.push(0); + } + instance.exports.bjs_roundTripOptionalClass(...results); + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + const optResult = pointer === null ? null : Greeter.__construct(pointer); + return optResult; + }, + testOptionalPropertyRoundtrip: function bjs_testOptionalPropertyRoundtrip(holder) { + const isNull = (holder === null || holder === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(holder.pointer); + } else { + results.push(0); + } + instance.exports.bjs_testOptionalPropertyRoundtrip(...results); + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + const optResult = pointer === null ? null : OptionalPropertyHolder.__construct(pointer); + return optResult; + }, + roundTripString: function bjs_roundTripString(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_roundTripString(...results); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId !== undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + roundTripInt: function bjs_roundTripInt(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(value); + } else { + results.push(0); + } + instance.exports.bjs_roundTripInt(...results); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, + roundTripBool: function bjs_roundTripBool(flag) { + const isNull = (flag === null || flag === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(flag); + } else { + results.push(0); + } + instance.exports.bjs_roundTripBool(...results); + const optResult = tmpRetOptionalBool; + tmpRetOptionalBool = undefined; + return optResult; + }, + roundTripFloat: function bjs_roundTripFloat(number) { + const isNull = (number === null || number === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(number); + } else { + results.push(0); + } + instance.exports.bjs_roundTripFloat(...results); + const optResult = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return optResult; + }, + roundTripDouble: function bjs_roundTripDouble(precision) { + const isNull = (precision === null || precision === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(precision); + } else { + results.push(0); + } + instance.exports.bjs_roundTripDouble(...results); + const optResult = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return optResult; + }, + roundTripSyntax: function bjs_roundTripSyntax(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_roundTripSyntax(...results); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId !== undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + roundTripMixSyntax: function bjs_roundTripMixSyntax(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_roundTripMixSyntax(...results); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId !== undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + roundTripSwiftSyntax: function bjs_roundTripSwiftSyntax(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_roundTripSwiftSyntax(...results); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId !== undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + roundTripMixedSwiftSyntax: function bjs_roundTripMixedSwiftSyntax(name) { + const isNull = (name === null || name === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let nameId; + if (!isNull) { + const nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + results.push(nameId); + results.push(nameBytes.length); + } else { + results.push(0); + results.push(0); + } + instance.exports.bjs_roundTripMixedSwiftSyntax(...results); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId !== undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + roundTripWithSpaces: function bjs_roundTripWithSpaces(value) { + const isNull = (value === null || value === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(value); + } else { + results.push(0); + } + instance.exports.bjs_roundTripWithSpaces(...results); + const optResult = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return optResult; + }, + roundTripAlias: function bjs_roundTripAlias(age) { + const isNull = (age === null || age === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + if (!isNull) { + results.push(age); + } else { + results.push(0); + } + instance.exports.bjs_roundTripAlias(...results); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, + testMixedOptionals: function bjs_testMixedOptionals(firstName, lastName) { + const isNull = (firstName === null || firstName === undefined); + const isSome = isNull ? 0 : 1; + let results = [isSome]; + let firstNameId; + if (!isNull) { + const firstNameBytes = textEncoder.encode(firstName); + firstNameId = swift.memory.retain(firstNameBytes); + results.push(firstNameId); + results.push(firstNameBytes.length); + } else { + results.push(0); + results.push(0); + } + const isNull1 = (lastName === null || lastName === undefined); + const isSome1 = isNull1 ? 0 : 1; + let results1 = [isSome1]; + let lastNameId; + if (!isNull1) { + const lastNameBytes = textEncoder.encode(lastName); + lastNameId = swift.memory.retain(lastNameBytes); + results1.push(lastNameId); + results1.push(lastNameBytes.length); + } else { + results1.push(0); + results1.push(0); + } + instance.exports.bjs_testMixedOptionals(...results, ...results1); + const optResult = tmpRetString; + tmpRetString = undefined; + if (firstNameId !== undefined) { + swift.memory.release(firstNameId); + } + if (lastNameId !== undefined) { + swift.memory.release(lastNameId); + } + return optResult; + }, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts new file mode 100644 index 00000000..e54856d7 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts @@ -0,0 +1,31 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export interface TestClass { + methodWithOptional(value: any): void; + methodReturningOptional(): any; + optionalProperty: any; +} +export type Exports = { +} +export type Imports = { + testOptionalNumber(value: any): void; + testOptionalString(value: any): void; + testOptionalBool(value: any): void; + testOptionalReturn(): any; + testOptionalNumberReturn(): any; + testMixedOptionals(required: string, optional: any): any; + TestClass: { + new(param: any): TestClass; + } +} +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/OptionalUnionTypes.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js new file mode 100644 index 00000000..288b3f1d --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js @@ -0,0 +1,249 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + + 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); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_testOptionalNumber"] = function bjs_testOptionalNumber(value) { + try { + imports.testOptionalNumber(swift.memory.getObject(value)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_testOptionalString"] = function bjs_testOptionalString(value) { + try { + imports.testOptionalString(swift.memory.getObject(value)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_testOptionalBool"] = function bjs_testOptionalBool(value) { + try { + imports.testOptionalBool(swift.memory.getObject(value)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_testOptionalReturn"] = function bjs_testOptionalReturn() { + try { + let ret = imports.testOptionalReturn(); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_testOptionalNumberReturn"] = function bjs_testOptionalNumberReturn() { + try { + let ret = imports.testOptionalNumberReturn(); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_testMixedOptionals"] = function bjs_testMixedOptionals(required, optional) { + try { + const requiredObject = swift.memory.getObject(required); + swift.memory.release(required); + let ret = imports.testMixedOptionals(requiredObject, swift.memory.getObject(optional)); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_TestClass_init"] = function bjs_TestClass_init(param) { + try { + return swift.memory.retain(new imports.TestClass(swift.memory.getObject(param))); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_TestClass_optionalProperty_get"] = function bjs_TestClass_optionalProperty_get(self) { + try { + let ret = swift.memory.getObject(self).optionalProperty; + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_TestClass_optionalProperty_set"] = function bjs_TestClass_optionalProperty_set(self, newValue) { + try { + swift.memory.getObject(self).optionalProperty = swift.memory.getObject(newValue); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_TestClass_methodWithOptional"] = function bjs_TestClass_methodWithOptional(self, value) { + try { + swift.memory.getObject(self).methodWithOptional(swift.memory.getObject(value)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_TestClass_methodReturningOptional"] = function bjs_TestClass_methodReturningOptional(self) { + try { + let ret = swift.memory.getObject(self).methodReturningOptional(); + return swift.memory.retain(ret); + } catch (error) { + setException(error); + return 0 + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + return { + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js index 8aeb3f9b..eea05ba1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js index c6e9c0de..882e633e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_check"] = function bjs_check(a, b) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js index 5bf2e0fd..7c20fa15 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js index 202fbb55..481cc495 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkNumber"] = function bjs_checkNumber() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js index 281bed1d..bbfbb0ac 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts index a83fca6f..22943671 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts @@ -6,6 +6,7 @@ export type Exports = { checkString(a: string): void; + roundtripString(a: string): string; } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js index cadd112c..b7d4b505 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; @@ -102,6 +157,15 @@ export async function createInstantiator(options, swift) { instance.exports.bjs_checkString(aId, aBytes.length); swift.memory.release(aId); }, + roundtripString: function bjs_roundtripString(a) { + const aBytes = textEncoder.encode(a); + const aId = swift.memory.retain(aBytes); + instance.exports.bjs_roundtripString(aId, aBytes.length); + const ret = tmpRetString; + tmpRetString = undefined; + swift.memory.release(aId); + return ret; + }, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js index bed39c8e..bc109fc9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkString"] = function bjs_checkString(a) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js index 565cd605..08eef7e0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js index 546b20f8..11d30446 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkString"] = function bjs_checkString() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js index 223c9e7b..0a545a0e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } // Wrapper functions for module: TestModule if (!importObject["TestModule"]) { importObject["TestModule"] = {}; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js index 9a568612..453d5f76 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TS2SkeletonLike.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_createTS2Skeleton"] = function bjs_createTS2Skeleton() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js index e6512ca4..af17ab14 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js index 9c3b5508..9ba7cf60 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_checkSimple"] = function bjs_checkSimple(a) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js index fccdccd1..98fb575d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_Greeter_init"] = function bjs_Greeter_init(name) { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js index 89515b44..d2716dfa 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } }, setInstance: (i) => { instance = i; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js index 54f3267a..4b4c75ea 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js @@ -13,6 +13,11 @@ export async function createInstantiator(options, swift) { let tmpRetString; let tmpRetBytes; let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; let tmpRetTag; let tmpRetStrings = []; let tmpRetInts = []; @@ -83,6 +88,56 @@ export async function createInstantiator(options, swift) { bjs["swift_js_pop_param_f64"] = function() { return tmpParamF64s.pop(); } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; TestModule["bjs_check"] = function bjs_check() { try { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json new file mode 100644 index 00000000..e3fac8ed --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json @@ -0,0 +1,612 @@ +{ + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_Greeter_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + } + ] + }, + "methods" : [ + { + "abiName" : "bjs_Greeter_greet", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "greet", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_Greeter_changeName", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "changeName", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "Greeter", + "properties" : [ + { + "isReadonly" : false, + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "swiftCallName" : "Greeter" + }, + { + "constructor" : { + "abiName" : "bjs_OptionalPropertyHolder_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + + ], + "name" : "OptionalPropertyHolder", + "properties" : [ + { + "isReadonly" : false, + "name" : "optionalName", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "name" : "optionalAge", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "name" : "optionalGreeter", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + ], + "swiftCallName" : "OptionalPropertyHolder" + } + ], + "enums" : [ + + ], + "functions" : [ + { + "abiName" : "bjs_roundTripOptionalClass", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalClass", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "abiName" : "bjs_testOptionalPropertyRoundtrip", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "testOptionalPropertyRoundtrip", + "parameters" : [ + { + "label" : "_", + "name" : "holder", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "OptionalPropertyHolder" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "OptionalPropertyHolder" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripString", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripString", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripInt", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripInt", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripBool", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripBool", + "parameters" : [ + { + "label" : "flag", + "name" : "flag", + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripFloat", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripFloat", + "parameters" : [ + { + "label" : "number", + "name" : "number", + "type" : { + "optional" : { + "_0" : { + "float" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "float" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripDouble", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripDouble", + "parameters" : [ + { + "label" : "precision", + "name" : "precision", + "type" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripMixSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripMixSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripSwiftSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripSwiftSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripMixedSwiftSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripMixedSwiftSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripWithSpaces", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripWithSpaces", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripAlias", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripAlias", + "parameters" : [ + { + "label" : "age", + "name" : "age", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "abiName" : "bjs_testMixedOptionals", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "testMixedOptionals", + "parameters" : [ + { + "label" : "firstName", + "name" : "firstName", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "label" : "lastName", + "name" : "lastName", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift new file mode 100644 index 00000000..891fc7f0 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift @@ -0,0 +1,328 @@ +// 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 + +@_expose(wasm, "bjs_roundTripOptionalClass") +@_cdecl("bjs_roundTripOptionalClass") +public func _bjs_roundTripOptionalClass(valueIsSome: Int32, valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testOptionalPropertyRoundtrip") +@_cdecl("bjs_testOptionalPropertyRoundtrip") +public func _bjs_testOptionalPropertyRoundtrip(holderIsSome: Int32, holderValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = testOptionalPropertyRoundtrip(_: Optional.bridgeJSLiftParameter(holderIsSome, holderValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripString") +@_cdecl("bjs_roundTripString") +public func _bjs_roundTripString(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripString(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripInt") +@_cdecl("bjs_roundTripInt") +public func _bjs_roundTripInt(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripInt(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripBool") +@_cdecl("bjs_roundTripBool") +public func _bjs_roundTripBool(flagIsSome: Int32, flagValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripBool(flag: Optional.bridgeJSLiftParameter(flagIsSome, flagValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripFloat") +@_cdecl("bjs_roundTripFloat") +public func _bjs_roundTripFloat(numberIsSome: Int32, numberValue: Float32) -> Void { + #if arch(wasm32) + let ret = roundTripFloat(number: Optional.bridgeJSLiftParameter(numberIsSome, numberValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripDouble") +@_cdecl("bjs_roundTripDouble") +public func _bjs_roundTripDouble(precisionIsSome: Int32, precisionValue: Float64) -> Void { + #if arch(wasm32) + let ret = roundTripDouble(precision: Optional.bridgeJSLiftParameter(precisionIsSome, precisionValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripSyntax") +@_cdecl("bjs_roundTripSyntax") +public func _bjs_roundTripSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripMixSyntax") +@_cdecl("bjs_roundTripMixSyntax") +public func _bjs_roundTripMixSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripMixSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripSwiftSyntax") +@_cdecl("bjs_roundTripSwiftSyntax") +public func _bjs_roundTripSwiftSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripSwiftSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripMixedSwiftSyntax") +@_cdecl("bjs_roundTripMixedSwiftSyntax") +public func _bjs_roundTripMixedSwiftSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripMixedSwiftSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripWithSpaces") +@_cdecl("bjs_roundTripWithSpaces") +public func _bjs_roundTripWithSpaces(valueIsSome: Int32, valueValue: Float64) -> Void { + #if arch(wasm32) + let ret = roundTripWithSpaces(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripAlias") +@_cdecl("bjs_roundTripAlias") +public func _bjs_roundTripAlias(ageIsSome: Int32, ageValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripAlias(age: Optional.bridgeJSLiftParameter(ageIsSome, ageValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testMixedOptionals") +@_cdecl("bjs_testMixedOptionals") +public func _bjs_testMixedOptionals(firstNameIsSome: Int32, firstNameBytes: Int32, firstNameLength: Int32, lastNameIsSome: Int32, lastNameBytes: Int32, lastNameLength: Int32) -> Void { + #if arch(wasm32) + let ret = testMixedOptionals(firstName: Optional.bridgeJSLiftParameter(firstNameIsSome, firstNameBytes, firstNameLength), lastName: Optional.bridgeJSLiftParameter(lastNameIsSome, lastNameBytes, lastNameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_init") +@_cdecl("bjs_Greeter_init") +public func _bjs_Greeter_init(nameBytes: Int32, nameLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = Greeter(name: String.bridgeJSLiftParameter(nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_greet") +@_cdecl("bjs_Greeter_greet") +public func _bjs_Greeter_greet(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = Greeter.bridgeJSLiftParameter(_self).greet() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_changeName") +@_cdecl("bjs_Greeter_changeName") +public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + Greeter.bridgeJSLiftParameter(_self).changeName(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_name_get") +@_cdecl("bjs_Greeter_name_get") +public func _bjs_Greeter_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = Greeter.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_name_set") +@_cdecl("bjs_Greeter_name_set") +public func _bjs_Greeter_name_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + Greeter.bridgeJSLiftParameter(_self).name = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_deinit") +@_cdecl("bjs_Greeter_deinit") +public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension Greeter: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_Greeter_wrap") + func _bjs_Greeter_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_Greeter_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_Greeter_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_init") +@_cdecl("bjs_OptionalPropertyHolder_init") +public func _bjs_OptionalPropertyHolder_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = OptionalPropertyHolder() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalName_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalName_get") +public func _bjs_OptionalPropertyHolder_optionalName_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalName + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalName_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalName_set") +public func _bjs_OptionalPropertyHolder_optionalName_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalName = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalAge_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalAge_get") +public func _bjs_OptionalPropertyHolder_optionalAge_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalAge + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalAge_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalAge_set") +public func _bjs_OptionalPropertyHolder_optionalAge_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalAge = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_get") +public func _bjs_OptionalPropertyHolder_optionalGreeter_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_set") +public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_deinit") +@_cdecl("bjs_OptionalPropertyHolder_deinit") +public func _bjs_OptionalPropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension OptionalPropertyHolder: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_OptionalPropertyHolder_wrap") + func _bjs_OptionalPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_OptionalPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_OptionalPropertyHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json index 22df1dc5..24d7641c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json @@ -27,6 +27,30 @@ "returnType" : { "void" : { + } + } + }, + { + "abiName" : "bjs_roundtripString", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundtripString", + "parameters" : [ + { + "label" : "a", + "name" : "a", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift index 723a639c..2a43d7bf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift @@ -14,4 +14,15 @@ public func _bjs_checkString(aBytes: Int32, aLength: Int32) -> Void { #else fatalError("Only available on WebAssembly") #endif +} + +@_expose(wasm, "bjs_roundtripString") +@_cdecl("bjs_roundtripString") +public func _bjs_roundtripString(aBytes: Int32, aLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundtripString(a: String.bridgeJSLiftParameter(aBytes, aLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift new file mode 100644 index 00000000..6c8ef5b9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift @@ -0,0 +1,189 @@ +// 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 + +func testOptionalNumber(_ value: JSObject) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testOptionalNumber") + func bjs_testOptionalNumber(_ value: Int32) -> Void + #else + func bjs_testOptionalNumber(_ value: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_testOptionalNumber(value.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } +} + +func testOptionalString(_ value: JSObject) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testOptionalString") + func bjs_testOptionalString(_ value: Int32) -> Void + #else + func bjs_testOptionalString(_ value: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_testOptionalString(value.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } +} + +func testOptionalBool(_ value: JSObject) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testOptionalBool") + func bjs_testOptionalBool(_ value: Int32) -> Void + #else + func bjs_testOptionalBool(_ value: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_testOptionalBool(value.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } +} + +func testOptionalReturn() throws(JSException) -> JSObject { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testOptionalReturn") + func bjs_testOptionalReturn() -> Int32 + #else + func bjs_testOptionalReturn() -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_testOptionalReturn() + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) +} + +func testOptionalNumberReturn() throws(JSException) -> JSObject { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testOptionalNumberReturn") + func bjs_testOptionalNumberReturn() -> Int32 + #else + func bjs_testOptionalNumberReturn() -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_testOptionalNumberReturn() + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) +} + +func testMixedOptionals(_ required: String, _ optional: JSObject) throws(JSException) -> JSObject { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_testMixedOptionals") + func bjs_testMixedOptionals(_ required: Int32, _ optional: Int32) -> Int32 + #else + func bjs_testMixedOptionals(_ required: Int32, _ optional: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_testMixedOptionals(required.bridgeJSLowerParameter(), optional.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) +} + +struct TestClass: _JSBridgedClass { + let jsObject: JSObject + + init(unsafelyWrapping jsObject: JSObject) { + self.jsObject = jsObject + } + + init(_ param: JSObject) throws(JSException) { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_TestClass_init") + func bjs_TestClass_init(_ param: Int32) -> Int32 + #else + func bjs_TestClass_init(_ param: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_TestClass_init(param.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + self.jsObject = JSObject(id: UInt32(bitPattern: ret)) + } + + var optionalProperty: JSObject { + get throws(JSException) { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_TestClass_optionalProperty_get") + func bjs_TestClass_optionalProperty_get(_ self: Int32) -> Int32 + #else + func bjs_TestClass_optionalProperty_get(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_TestClass_optionalProperty_get(self.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) + } + } + + func setOptionalProperty(_ newValue: JSObject) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_TestClass_optionalProperty_set") + func bjs_TestClass_optionalProperty_set(_ self: Int32, _ newValue: Int32) -> Void + #else + func bjs_TestClass_optionalProperty_set(_ self: Int32, _ newValue: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_TestClass_optionalProperty_set(self.bridgeJSLowerParameter(), newValue.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + } + + func methodWithOptional(_ value: JSObject) throws(JSException) -> Void { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_TestClass_methodWithOptional") + func bjs_TestClass_methodWithOptional(_ self: Int32, _ value: Int32) -> Void + #else + func bjs_TestClass_methodWithOptional(_ self: Int32, _ value: Int32) -> Void { + fatalError("Only available on WebAssembly") + } + #endif + bjs_TestClass_methodWithOptional(self.bridgeJSLowerParameter(), value.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + } + + func methodReturningOptional() throws(JSException) -> JSObject { + #if arch(wasm32) + @_extern(wasm, module: "Check", name: "bjs_TestClass_methodReturningOptional") + func bjs_TestClass_methodReturningOptional(_ self: Int32) -> Int32 + #else + func bjs_TestClass_methodReturningOptional(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + let ret = bjs_TestClass_methodReturningOptional(self.bridgeJSLowerParameter()) + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) + } + +} \ No newline at end of file diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index 31f56a2a..d7026ac0 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -372,7 +372,7 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo @_extern(wasm, module: "bjs", name: "swift_js_push_int") @_spi(BridgeJS) public func _swift_js_push_int(_ value: Int32) #else -@_spi(BridgeJS) public func _swift_js_return_int(_ value: Int32) { +@_spi(BridgeJS) public func _swift_js_push_int(_ value: Int32) { _onlyAvailableOnWasm() } #endif @@ -399,7 +399,7 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo @_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() { +@_spi(BridgeJS) public func _swift_js_pop_param_int32() -> Int32 { _onlyAvailableOnWasm() } #endif @@ -408,7 +408,7 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo @_extern(wasm, module: "bjs", name: "swift_js_pop_param_f32") @_spi(BridgeJS) public func _swift_js_pop_param_f32() -> Float32 #else -@_spi(BridgeJS) public func _swift_js_pop_param_f32() { +@_spi(BridgeJS) public func _swift_js_pop_param_f32() -> Float32 { _onlyAvailableOnWasm() } #endif @@ -417,7 +417,351 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo @_extern(wasm, module: "bjs", name: "swift_js_pop_param_f64") @_spi(BridgeJS) public func _swift_js_pop_param_f64() -> Float64 #else -@_spi(BridgeJS) public func _swift_js_pop_param_f64() { +@_spi(BridgeJS) public func _swift_js_pop_param_f64() -> Float64 { _onlyAvailableOnWasm() } #endif + +// MARK: Optional Type Support + +extension Optional where Wrapped == Bool { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { + switch self { + case .none: + return (0, 0) // isSome=0, dummy value=0 + case .some(let value): + return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Bool? { + if isSome == 0 { + return nil + } else { + return Bool.bridgeJSLiftReturn(wrappedValue) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Bool? { + if isSome == 0 { + return nil + } else { + return Bool.bridgeJSLiftParameter(wrappedValue) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_bool") + func _swift_js_return_optional_bool(_ isSome: Int32, _ value: Int32) + #else + /// Sets the optional bool for return value storage + func _swift_js_return_optional_bool(_ isSome: Int32, _ value: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_return_optional_bool(0, 0) + case .some(let value): + _swift_js_return_optional_bool(1, value.bridgeJSLowerReturn()) + } + } +} + +/// Optional support for Int +extension Optional where Wrapped == Int { + // MARK: ImportTS + + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> (Int32, Int32) { + switch self { + case .none: + return (0, 0) // isSome=0, dummy value=0 + case .some(let value): + return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Int? { + if isSome == 0 { + return nil + } else { + return Int.bridgeJSLiftReturn(wrappedValue) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Int? { + if isSome == 0 { + return nil + } else { + return Int.bridgeJSLiftParameter(wrappedValue) + } + } + + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_int") + func _swift_js_return_optional_int(_ isSome: Int32, _ value: Int32) + #else + /// Sets the optional int for return value storage + func _swift_js_return_optional_int(_ isSome: Int32, _ value: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch self { + case .none: + _swift_js_return_optional_int(0, 0) + case .some(let value): + _swift_js_return_optional_int(1, value.bridgeJSLowerReturn()) + } + } +} +extension Optional where Wrapped == String { + // MARK: ImportTS + + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Int32 { + switch self { + case .none: + return 0 // Return special sentinel value for nil string + case .some(let value): + return value.bridgeJSLowerParameter() + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> String? { + if isSome == 0 { + return nil + } else { + return String.bridgeJSLiftReturn(isSome) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ bytes: Int32, _ count: Int32) -> String? { + if isSome == 0 { + return nil + } else { + return String.bridgeJSLiftParameter(bytes, count) + } + } + + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_string") + func _swift_js_return_optional_string(_ isSome: Int32, _ ptr: UnsafePointer?, _ len: Int32) + #else + /// Write an optional string to reserved string storage to be returned to JavaScript + func _swift_js_return_optional_string(_ isSome: Int32, _ ptr: UnsafePointer?, _ len: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch self { + case .none: + _swift_js_return_optional_string(0, nil, 0) + case .some(var value): + return value.withUTF8 { ptr in + _swift_js_return_optional_string(1, ptr.baseAddress, Int32(ptr.count)) + } + } + } +} +extension Optional where Wrapped == JSObject { + // MARK: ImportTS + + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> (Int32, Int32) { + switch self { + case .none: + return (0, 0) + case .some(let value): + return (1, value.bridgeJSLowerParameter()) + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ objectId: Int32) -> JSObject? { + if isSome == 0 { + return nil + } else { + return JSObject.bridgeJSLiftReturn(objectId) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> JSObject? { + if isSome == 0 { + return nil + } else { + return JSObject.bridgeJSLiftParameter(objectId) + } + } + + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_object") + func _swift_js_return_optional_object(_ isSome: Int32, _ objectId: Int32) + #else + /// Write an optional JSObject to reserved storage to be returned to JavaScript + func _swift_js_return_optional_object(_ isSome: Int32, _ objectId: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch self { + case .none: + _swift_js_return_optional_object(0, 0) + case .some(let value): + let retainedId = value.bridgeJSLowerReturn() + _swift_js_return_optional_object(1, retainedId) + } + } +} + +/// Optional support for Swift heap objects +extension Optional where Wrapped: _BridgedSwiftHeapObject { + // MARK: ImportTS + @available(*, unavailable, message: "Optional Swift heap objects are not supported to be passed to imported JS functions") + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional Swift heap objects are not supported to be returned from imported JS functions") + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer) -> Void {} + + // MARK: ExportSwift + + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer) -> Optional { + if isSome == 0 { + return nil + } else { + return Wrapped.bridgeJSLiftParameter(pointer) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_heap_object") + func _swift_js_return_optional_heap_object(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer?) + #else + /// Write an optional Swift heap object to reserved storage to be returned to JavaScript + func _swift_js_return_optional_heap_object(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer?) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_return_optional_heap_object(0, nil) + case .some(let value): + let retainedPointer = value.bridgeJSLowerReturn() + _swift_js_return_optional_heap_object(1, retainedPointer) + } + } +} +extension Optional where Wrapped == Float { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float32) { + switch self { + case .none: + return (0, 0.0) + case .some(let value): + return (1, value.bridgeJSLowerParameter()) + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float32) -> Float? { + if isSome == 0 { + return nil + } else { + return Float.bridgeJSLiftReturn(wrappedValue) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Float? { + if isSome == 0 { + return nil + } else { + return Float.bridgeJSLiftParameter(wrappedValue) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_float") + func _swift_js_return_optional_float(_ isSome: Int32, _ value: Float32) + #else + /// Sets the optional float for return value storage + func _swift_js_return_optional_float(_ isSome: Int32, _ value: Float32) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_return_optional_float(0, 0.0) + case .some(let value): + _swift_js_return_optional_float(1, value.bridgeJSLowerReturn()) + } + } +} + +/// Optional support for Double +extension Optional where Wrapped == Double { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float64) { + switch self { + case .none: + return (0, 0.0) // isSome=0, dummy value=0.0 + case .some(let value): + return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float64) -> Double? { + if isSome == 0 { + return nil + } else { + return Double.bridgeJSLiftReturn(wrappedValue) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Double? { + if isSome == 0 { + return nil + } else { + return Double.bridgeJSLiftParameter(wrappedValue) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_double") + func _swift_js_return_optional_double(_ isSome: Int32, _ value: Float64) + #else + /// Sets the optional double for return value storage + func _swift_js_return_optional_double(_ isSome: Int32, _ value: Float64) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_return_optional_double(0, 0.0) + case .some(let value): + _swift_js_return_optional_double(1, value.bridgeJSLowerReturn()) + } + } +} diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index b00aa67b..e6271e00 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -435,6 +435,63 @@ enum ComplexResult { return result } +// MARK: - Optionals + +@JS func roundTripOptionalString(name: String?) -> String? { + return name +} + +@JS func roundTripOptionalInt(value: Int?) -> Int? { + return value +} + +@JS func roundTripOptionalBool(flag: Bool?) -> Bool? { + return flag +} + +@JS func roundTripOptionalFloat(number: Float?) -> Float? { + return number +} + +@JS func roundTripOptionalDouble(precision: Double?) -> Double? { + return precision +} + +@JS func roundTripOptionalSyntax(name: Optional) -> Optional { + return name +} + +@JS func roundTripOptionalMixSyntax(name: String?) -> Optional { + return name +} + +@JS func roundTripOptionalSwiftSyntax(name: Swift.Optional) -> Swift.Optional { + return name +} + +@JS func roundTripOptionalWithSpaces(value: Optional< Double >) -> Optional< Double > { + return value +} + +typealias OptionalAge = Int? +@JS func roundTripOptionalTypeAlias(age: OptionalAge) -> OptionalAge { + return age +} + +@JS func roundTripOptionalClass(value: Greeter?) -> Greeter? { + return value +} + +@JS class OptionalPropertyHolder { + @JS var optionalName: String? + @JS var optionalAge: Int? = nil + @JS var optionalGreeter: Greeter? = nil + + @JS init(optionalName: String?) { + self.optionalName = optionalName + } +} + // 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 5ae4e055..41ba24a6 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -1310,6 +1310,127 @@ public func _bjs_roundtripAPINetworkingResult(result: Int32) -> Void { #endif } +@_expose(wasm, "bjs_roundTripOptionalString") +@_cdecl("bjs_roundTripOptionalString") +public func _bjs_roundTripOptionalString(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalString(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalInt") +@_cdecl("bjs_roundTripOptionalInt") +public func _bjs_roundTripOptionalInt(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalInt(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalBool") +@_cdecl("bjs_roundTripOptionalBool") +public func _bjs_roundTripOptionalBool(flagIsSome: Int32, flagValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalBool(flag: Optional.bridgeJSLiftParameter(flagIsSome, flagValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalFloat") +@_cdecl("bjs_roundTripOptionalFloat") +public func _bjs_roundTripOptionalFloat(numberIsSome: Int32, numberValue: Float32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalFloat(number: Optional.bridgeJSLiftParameter(numberIsSome, numberValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalDouble") +@_cdecl("bjs_roundTripOptionalDouble") +public func _bjs_roundTripOptionalDouble(precisionIsSome: Int32, precisionValue: Float64) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalDouble(precision: Optional.bridgeJSLiftParameter(precisionIsSome, precisionValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalSyntax") +@_cdecl("bjs_roundTripOptionalSyntax") +public func _bjs_roundTripOptionalSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalMixSyntax") +@_cdecl("bjs_roundTripOptionalMixSyntax") +public func _bjs_roundTripOptionalMixSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalMixSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalSwiftSyntax") +@_cdecl("bjs_roundTripOptionalSwiftSyntax") +public func _bjs_roundTripOptionalSwiftSyntax(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalSwiftSyntax(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalWithSpaces") +@_cdecl("bjs_roundTripOptionalWithSpaces") +public func _bjs_roundTripOptionalWithSpaces(valueIsSome: Int32, valueValue: Float64) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalWithSpaces(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalTypeAlias") +@_cdecl("bjs_roundTripOptionalTypeAlias") +public func _bjs_roundTripOptionalTypeAlias(ageIsSome: Int32, ageValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTypeAlias(age: Optional.bridgeJSLiftParameter(ageIsSome, ageValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalClass") +@_cdecl("bjs_roundTripOptionalClass") +public func _bjs_roundTripOptionalClass(valueIsSome: Int32, valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + 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 { @@ -1663,6 +1784,100 @@ extension Internal.TestServer: ConvertibleToJSValue, _BridgedSwiftHeapObject { } } +@_expose(wasm, "bjs_OptionalPropertyHolder_init") +@_cdecl("bjs_OptionalPropertyHolder_init") +public func _bjs_OptionalPropertyHolder_init(optionalNameIsSome: Int32, optionalNameBytes: Int32, optionalNameLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = OptionalPropertyHolder(optionalName: Optional.bridgeJSLiftParameter(optionalNameIsSome, optionalNameBytes, optionalNameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalName_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalName_get") +public func _bjs_OptionalPropertyHolder_optionalName_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalName + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalName_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalName_set") +public func _bjs_OptionalPropertyHolder_optionalName_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalName = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalAge_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalAge_get") +public func _bjs_OptionalPropertyHolder_optionalAge_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalAge + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalAge_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalAge_set") +public func _bjs_OptionalPropertyHolder_optionalAge_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalAge = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_get") +@_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_get") +public func _bjs_OptionalPropertyHolder_optionalGreeter_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_set") +@_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_set") +public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalPropertyHolder_deinit") +@_cdecl("bjs_OptionalPropertyHolder_deinit") +public func _bjs_OptionalPropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension OptionalPropertyHolder: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_OptionalPropertyHolder_wrap") + func _bjs_OptionalPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_OptionalPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_OptionalPropertyHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + @_expose(wasm, "bjs_SimplePropertyHolder_init") @_cdecl("bjs_SimplePropertyHolder_init") public func _bjs_SimplePropertyHolder_init(value: Int32) -> UnsafeMutableRawPointer { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index 5609a0f2..b52d7811 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -324,6 +324,76 @@ ], "swiftCallName" : "Internal.TestServer" }, + { + "constructor" : { + "abiName" : "bjs_OptionalPropertyHolder_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "optionalName", + "name" : "optionalName", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "OptionalPropertyHolder", + "properties" : [ + { + "isReadonly" : false, + "name" : "optionalName", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "name" : "optionalAge", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "name" : "optionalGreeter", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + ], + "swiftCallName" : "OptionalPropertyHolder" + }, { "constructor" : { "abiName" : "bjs_SimplePropertyHolder_init", @@ -3006,6 +3076,358 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalString", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalString", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalInt", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalInt", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalBool", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalBool", + "parameters" : [ + { + "label" : "flag", + "name" : "flag", + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalFloat", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalFloat", + "parameters" : [ + { + "label" : "number", + "name" : "number", + "type" : { + "optional" : { + "_0" : { + "float" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "float" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalDouble", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalDouble", + "parameters" : [ + { + "label" : "precision", + "name" : "precision", + "type" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalMixSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalMixSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalSwiftSyntax", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalSwiftSyntax", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalWithSpaces", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalWithSpaces", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalTypeAlias", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTypeAlias", + "parameters" : [ + { + "label" : "age", + "name" : "age", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalClass", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalClass", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, { "abiName" : "bjs_createPropertyHolder", "effects" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 5ce68b5c..1065be50 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -229,26 +229,26 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(ph.lazyValue, "computed lazily"); ph.lazyValue = "modified lazy"; assert.equal(ph.lazyValue, "modified lazy"); - + // Test computed read-write property assert.equal(ph.computedReadWrite, "Value: 456"); ph.computedReadWrite = "Value: 777"; assert.equal(ph.intValue, 777); // Should have parsed and set intValue assert.equal(ph.computedReadWrite, "Value: 777"); - + // Test computed readonly property assert.equal(ph.computedReadonly, 1554); // intValue * 2 = 777 * 2 - + // Test property with observers // Sync observedProperty to match current intValue, then reset counters for clean test ph.observedProperty = 777; // Sync with current intValue after computed property changed it exports.resetObserverCounts(); // Reset counters to start fresh test - + // Set property from JavaScript and verify observers are called ph.observedProperty = 100; assert.equal(ph.observedProperty, 100); let afterSetStats = exports.getObserverStats(); - + // Verify willSet and didSet were called // The stats should show: willSet:1,didSet:1,willSetOld:777,willSetNew:100,didSetOld:777,didSetNew:100 assert(afterSetStats.includes("willSet:1"), `willSet should be called once, got: ${afterSetStats}`); @@ -257,12 +257,12 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert(afterSetStats.includes("willSetNew:100"), `willSet should see new value 100, got: ${afterSetStats}`); assert(afterSetStats.includes("didSetOld:777"), `didSet should see old value 777, got: ${afterSetStats}`); assert(afterSetStats.includes("didSetNew:100"), `didSet should see new value 100, got: ${afterSetStats}`); - + // Set property to a different value and verify observers are called again ph.observedProperty = 200; assert.equal(ph.observedProperty, 200); let afterSecondSetStats = exports.getObserverStats(); - + // Now should be: willSet:2,didSet:2,willSetOld:100,willSetNew:200,didSetOld:100,didSetNew:200 assert(afterSecondSetStats.includes("willSet:2"), `willSet should be called twice, got: ${afterSecondSetStats}`); assert(afterSecondSetStats.includes("didSet:2"), `didSet should be called twice, got: ${afterSecondSetStats}`); @@ -463,6 +463,64 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { 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 }); + + assert.equal(exports.roundTripOptionalString(null), null); + assert.equal(exports.roundTripOptionalInt(null), null); + assert.equal(exports.roundTripOptionalBool(null), null); + assert.equal(exports.roundTripOptionalFloat(null), null); + assert.equal(exports.roundTripOptionalDouble(null), null); + + assert.equal(exports.roundTripOptionalString("Hello"), "Hello"); + assert.equal(exports.roundTripOptionalInt(42), 42); + assert.equal(exports.roundTripOptionalBool(true), true); + assert.equal(exports.roundTripOptionalFloat(3.141592502593994), 3.141592502593994); // Float32 precision + assert.equal(exports.roundTripOptionalDouble(2.718), 2.718); + + assert.equal(exports.roundTripOptionalSyntax(null), null); + assert.equal(exports.roundTripOptionalSyntax("Test"), "Test"); + assert.equal(exports.roundTripOptionalMixSyntax(null), null); + assert.equal(exports.roundTripOptionalMixSyntax("Mix"), "Mix"); + assert.equal(exports.roundTripOptionalSwiftSyntax(null), null); + assert.equal(exports.roundTripOptionalSwiftSyntax("Swift"), "Swift"); + assert.equal(exports.roundTripOptionalWithSpaces(null), null); + assert.equal(exports.roundTripOptionalWithSpaces(1.618), 1.618); + + assert.equal(exports.roundTripOptionalTypeAlias(null), null); + assert.equal(exports.roundTripOptionalTypeAlias(25), 25); + + const optionalGreeter = new exports.Greeter("Schrödinger"); + const optionalGreeter2 = exports.roundTripOptionalClass(optionalGreeter); + assert.equal(optionalGreeter2?.greet() ?? "", "Hello, Schrödinger!"); + assert.equal(optionalGreeter2?.name ?? "", "Schrödinger"); + assert.equal(optionalGreeter2?.prefix ?? "", "Hello"); + assert.equal(exports.roundTripOptionalClass(null), null); + optionalGreeter.release(); + optionalGreeter2?.release(); + + const optionalsHolder = new exports.OptionalPropertyHolder(null); + + assert.equal(optionalsHolder.optionalName, null); + assert.equal(optionalsHolder.optionalAge, null); + assert.equal(optionalsHolder.optionalGreeter, null); + + optionalsHolder.optionalName = "Alice"; + optionalsHolder.optionalAge = 25; + assert.equal(optionalsHolder.optionalName, "Alice"); + assert.equal(optionalsHolder.optionalAge, 25); + + const testPropertyGreeter = new exports.Greeter("Bob"); + optionalsHolder.optionalGreeter = testPropertyGreeter; + assert.equal(optionalsHolder.optionalGreeter.greet(), "Hello, Bob!"); + assert.equal(optionalsHolder.optionalGreeter.name, "Bob"); + + optionalsHolder.optionalName = null; + optionalsHolder.optionalAge = null; + optionalsHolder.optionalGreeter = null; + assert.equal(optionalsHolder.optionalName, null); + assert.equal(optionalsHolder.optionalAge, null); + assert.equal(optionalsHolder.optionalGreeter, null); + testPropertyGreeter.release(); + optionalsHolder.release(); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ From 1e276d51f5aebcd240f6d544b0616dbce90674f7 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Thu, 4 Sep 2025 17:51:43 +0200 Subject: [PATCH 2/5] BridgeJS: Optional enums with associated value support BridgeJS: Optional case enum, optional raw enum support BridgeJS: Simplify JS glue code as much as possible + basic docs --- .../Sources/BridgeJSCore/ExportSwift.swift | 37 +- .../Sources/BridgeJSCore/ImportTS.swift | 75 +--- .../Sources/BridgeJSLink/JSGlueGen.swift | 237 +++++----- .../Inputs/EnumAssociatedValue.swift | 14 + .../BridgeJSToolTests/Inputs/EnumCase.swift | 2 + .../Inputs/EnumRawType.swift | 15 +- .../Inputs/OptionalUnionTypes.d.ts | 17 +- .../EnumAssociatedValue.Export.d.ts | 4 + .../EnumAssociatedValue.Export.js | 76 ++++ .../BridgeJSLinkTests/EnumCase.Export.d.ts | 2 + .../BridgeJSLinkTests/EnumCase.Export.js | 14 + .../BridgeJSLinkTests/EnumRawType.Export.d.ts | 14 +- .../BridgeJSLinkTests/EnumRawType.Export.js | 105 ++++- .../OptionalParameters.Export.js | 298 ++++--------- .../OptionalUnionTypes.Import.d.ts | 9 +- .../OptionalUnionTypes.Import.js | 27 +- .../ExportSwiftTests/EnumAssociatedValue.json | 128 ++++++ .../EnumAssociatedValue.swift | 68 ++- .../ExportSwiftTests/EnumCase.json | 64 +++ .../ExportSwiftTests/EnumCase.swift | 30 +- .../ExportSwiftTests/EnumNamespace.swift | 4 +- .../ExportSwiftTests/EnumRawType.json | 416 ++++++++++++++++-- .../ExportSwiftTests/EnumRawType.swift | 139 +++++- .../ImportTSTests/OptionalUnionTypes.swift | 57 +-- .../JavaScriptKit/BridgeJSInstrincics.swift | 271 +++++++++++- .../BridgeJS/Exporting-Swift-to-JavaScript.md | 1 + .../Exporting-Swift-Optional.md | 127 ++++++ .../Articles/BridgeJS/Supported-Types.md | 2 +- .../BridgeJSRuntimeTests/ExportAPITests.swift | 33 +- .../Generated/BridgeJS.ExportSwift.swift | 122 ++++- .../JavaScript/BridgeJS.ExportSwift.json | 262 +++++++++++ Tests/prelude.mjs | 9 +- 32 files changed, 2113 insertions(+), 566 deletions(-) create mode 100644 Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Optional.md diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 4b725289..3966415a 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -735,15 +735,14 @@ public class ExportSwift { } func lookupType(for type: TypeSyntax) -> BridgeType? { - // 1. Handle T? syntax (OptionalTypeSyntax) + // T? if let optionalType = type.as(OptionalTypeSyntax.self) { let wrappedType = optionalType.wrappedType if let baseType = lookupType(for: wrappedType) { return .optional(baseType) } } - - // 2. Handle Optional syntax (IdentifierTypeSyntax with generic arguments) + // Optional if let identifierType = type.as(IdentifierTypeSyntax.self), identifierType.name.text == "Optional", let genericArgs = identifierType.genericArgumentClause?.arguments, @@ -753,8 +752,7 @@ public class ExportSwift { return .optional(baseType) } } - - // 3. Handle Swift.Optional syntax (MemberTypeSyntax) + // Swift.Optional if let memberType = type.as(MemberTypeSyntax.self), let baseType = memberType.baseType.as(IdentifierTypeSyntax.self), baseType.name.text == "Swift", @@ -766,22 +764,17 @@ public class ExportSwift { return .optional(wrappedType) } } - - // 4. Handle type aliases - try to resolve through type declaration resolver first if let aliasDecl = typeDeclResolver.resolveTypeAlias(type) { - // Recursively lookup the aliased type if let resolvedType = lookupType(for: aliasDecl.initializer.value) { return resolvedType } } - // 5. Handle primitive types before falling back to other type declarations let typeName = type.trimmedDescription if let primitiveType = BridgeType.primitive(swiftType: typeName) { return primitiveType } - // 6. Try to resolve other type declarations guard let typeDecl = typeDeclResolver.resolve(type) else { return nil } @@ -898,7 +891,6 @@ public class ExportSwift { argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter } } - // For optional types, use Optional syntax instead of WrappedType? let typeNameForIntrinsic: String switch param.type { case .optional(let wrappedType): @@ -1004,7 +996,6 @@ public class ExportSwift { return } - // Handle optional types that use special storage functions and return Void if case .optional(_) = returnType { append("return ret.bridgeJSLowerReturn()") return @@ -1095,7 +1086,7 @@ public class ExportSwift { let valueSwitch = (["switch self {"] + valueCases + ["}"]).joined(separator: "\n") return """ - extension \(raw: typeName) { + extension \(raw: typeName): _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -1123,15 +1114,15 @@ public class ExportSwift { func renderAssociatedValueEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax { let typeName = enumDef.swiftCallName return """ - private extension \(raw: typeName) { - static func bridgeJSLiftParameter(_ caseId: Int32) -> \(raw: typeName) { + extension \(raw: typeName): _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> \(raw: typeName) { switch caseId { \(raw: generateStackLiftSwitchCases(enumDef: enumDef).joined(separator: "\n")) default: fatalError("Unknown \(raw: typeName) case ID: \\(caseId)") } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { \(raw: generateReturnSwitchCases(enumDef: enumDef).joined(separator: "\n")) } @@ -1168,7 +1159,7 @@ public class ExportSwift { case .double: return "\(paramName)Double.bridgeJSLiftParameter(_swift_js_pop_param_f64())" case .optional(_): - return "/* Optional associated value lifting not yet implemented */nil" // Placeholder + return "nil" default: return "\(paramName)Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())" } @@ -1205,8 +1196,6 @@ public class ExportSwift { bodyLines.append("_swift_js_push_f32(\(paramName))") case .double: bodyLines.append("_swift_js_push_f64(\(paramName))") - case .optional(_): - bodyLines.append("// Optional associated value lowering not yet implemented") // Placeholder default: bodyLines.append( "preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")" @@ -1494,7 +1483,7 @@ extension BridgeType { case .jsObject(let name?): return name case .swiftHeapObject(let name): return name case .void: return "Void" - case .optional(let wrappedType): return "\(wrappedType.swiftType)?" + case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>" case .caseEnum(let name): return name case .rawValueEnum(let name, _): return name case .associatedValueEnum(let name): return name @@ -1531,7 +1520,6 @@ extension BridgeType { case .swiftHeapObject: return .swiftHeapObject case .void: return .void case .optional(let wrappedType): - // Optional parameters include optionality flag plus wrapped type parameters var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters) return LiftingIntrinsicInfo(parameters: optionalParams) @@ -1570,6 +1558,7 @@ extension BridgeType { static let caseEnum = LoweringIntrinsicInfo(returnType: .i32) static let rawValueEnum = LoweringIntrinsicInfo(returnType: .i32) static let associatedValueEnum = LoweringIntrinsicInfo(returnType: nil) + static let optional = LoweringIntrinsicInfo(returnType: nil) } func loweringReturnInfo() throws -> LoweringIntrinsicInfo { @@ -1582,9 +1571,7 @@ extension BridgeType { case .jsObject: return .jsObject case .swiftHeapObject: return .swiftHeapObject case .void: return .void - case .optional(_): - // Optional return values use special storage functions and return void - return LoweringIntrinsicInfo(returnType: nil) + case .optional: return .optional case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): switch rawType { @@ -1602,7 +1589,7 @@ extension BridgeType { case .associatedValueEnum: return .associatedValueEnum case .namespaceEnum: - throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") + throw BridgeJSCoreError("Namespace enums are not supported as return types") } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 2fd27fce..abacddd0 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -72,51 +72,18 @@ public struct ImportTS { func lowerParameter(param: Parameter) throws { let loweringInfo = try param.type.loweringParameterInfo() - - // Handle void parameters (empty loweredParameters array) - if loweringInfo.loweredParameters.isEmpty { - // Void parameters don't generate any ABI parameters - return - } else if loweringInfo.loweredParameters.count == 1 { - // Simple case: single parameter - let (_, type) = loweringInfo.loweredParameters[0] - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") - ) - ) - abiParameterSignatures.append((param.name, type)) - } else { - // Complex case: multiple parameters (e.g., optionals with isSome flag + wrapped parameters) - // For optional types, use Optional syntax for the lowering call - let typeNameForLowering: String - switch param.type { - case .optional(let wrappedType): - typeNameForLowering = "Optional<\(wrappedType.swiftType)>" - default: - typeNameForLowering = param.type.swiftType - } - - let paramNames = loweringInfo.loweredParameters.enumerated().map { index, paramInfo in - if index == 0 { - return param.name + paramInfo.name.capitalizedFirstLetter - } else { - return param.name + paramInfo.name.capitalizedFirstLetter - } - } - - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: typeNameForLowering).bridgeJSLowerParameter(\(raw: param.name))") - ) + assert( + loweringInfo.loweredParameters.count == 1, + "For now, we require a single parameter to be lowered to a single Wasm core type" + ) + let (_, type) = loweringInfo.loweredParameters[0] + abiParameterForwardings.append( + LabeledExprSyntax( + label: param.label, + expression: ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") ) - - for (paramName, (_, type)) in zip(paramNames, loweringInfo.loweredParameters) { - abiParameterSignatures.append((paramName, type)) - } - } + ) + abiParameterSignatures.append((param.name, type)) } func call(returnType: BridgeType) { @@ -136,13 +103,6 @@ public struct ImportTS { if returnType == .void { return } - - // Handle optional return values that use special storage functions - if case .optional(_) = returnType { - body.append("return \(raw: returnType.swiftType).bridgeJSLiftReturn()") - return - } - body.append("return \(raw: returnType.swiftType).bridgeJSLiftReturn(ret)") } @@ -473,16 +433,12 @@ extension BridgeType { case .string: return .string case .jsObject: return .jsObject case .void: return .void - case .optional(let wrappedType): - // Optional parameters include optionality flag plus wrapped type parameters - let wrappedInfo = try wrappedType.loweringParameterInfo() - var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] - optionalParams.append(contentsOf: wrappedInfo.loweredParameters) - return LoweringParameterInfo(loweredParameters: optionalParams) case .swiftHeapObject: throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures") case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum: throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") + case .optional: + throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports") } } @@ -507,13 +463,12 @@ extension BridgeType { case .string: return .string case .jsObject: return .jsObject case .void: return .void - case .optional(_): - // Optional return values use special storage functions and return void - return LiftingReturnInfo(valueToLift: nil) case .swiftHeapObject: throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures") case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum: throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") + case .optional: + throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports") } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 932ee496..883dfe96 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -248,6 +248,132 @@ struct IntrinsicJSFragment: Sendable { } ) } + + static func optionalLowerParameter(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanupCode in + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(value) != null;") + + switch wrappedType { + case .string, .rawValueEnum(_, .string): + let bytesVar = scope.variable("\(value)Bytes") + let idVar = scope.variable("\(value)Id") + + printer.write("let \(idVar), \(bytesVar);") + printer.write("if (\(isSomeVar)) {") + printer.indent { + printer.write("\(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));") + printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") + } + printer.write("}") + cleanupCode.write("if (\(idVar) != undefined) {") + cleanupCode.indent { + cleanupCode.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") + } + cleanupCode.write("}") + + return ["+\(isSomeVar)", "\(isSomeVar) ? \(idVar) : 0", "\(isSomeVar) ? \(bytesVar).length : 0"] + case .associatedValueEnum(let fullName): + let base = fullName.components(separatedBy: ".").last ?? fullName + let caseIdVar = scope.variable("\(value)CaseId") + let cleanupVar = scope.variable("\(value)Cleanup") + + printer.write("let \(caseIdVar), \(cleanupVar);") + printer.write("if (\(isSomeVar)) {") + printer.indent { + let resultVar = scope.variable("enumResult") + printer.write("const \(resultVar) = enumHelpers.\(base).lower(\(value));") + printer.write("\(caseIdVar) = \(resultVar).caseId;") + printer.write("\(cleanupVar) = \(resultVar).cleanup;") + } + printer.write("}") + cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") + + return ["+\(isSomeVar)", "\(isSomeVar) ? \(caseIdVar) : 0"] + default: + switch wrappedType { + case .swiftHeapObject: + return ["+\(isSomeVar)", "\(isSomeVar) ? \(value).pointer : 0"] + default: + return ["+\(isSomeVar)", "\(isSomeVar) ? \(value) : 0"] + } + } + } + ) + } + + static func optionalLiftReturn(wrappedType: BridgeType) -> IntrinsicJSFragment { + return IntrinsicJSFragment( + parameters: [], + printCode: { arguments, scope, printer, cleanupCode in + let resultVar = scope.variable("optResult") + switch wrappedType { + case .bool: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") + case .int: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") + case .float: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") + case .double: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") + case .string: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") + case .swiftHeapObject(let className): + let pointerVar = scope.variable("pointer") + printer.write("const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;") + printer.write("const \(resultVar) = \(pointerVar) === null ? null : \(className).__construct(\(pointerVar));") + case .caseEnum: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") + case .rawValueEnum(_, let rawType): + switch rawType { + case .string: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") + case .bool: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") + case .float: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") + case .double: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") + default: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") + } + case .associatedValueEnum(let fullName): + let base = fullName.components(separatedBy: ".").last ?? fullName + let isNullVar = scope.variable("isNull") + printer.write("const \(isNullVar) = (\(JSGlueVariableScope.reservedTmpRetTag) === -1);") + printer.write("let \(resultVar);") + printer.write("if (\(isNullVar)) {") + printer.indent { + printer.write("\(resultVar) = null;") + } + printer.write("} else {") + printer.indent { + printer.write("\(resultVar) = enumHelpers.\(base).raise(\(JSGlueVariableScope.reservedTmpRetTag), \(JSGlueVariableScope.reservedTmpRetStrings), \(JSGlueVariableScope.reservedTmpRetInts), \(JSGlueVariableScope.reservedTmpRetF32s), \(JSGlueVariableScope.reservedTmpRetF64s));") + } + printer.write("}") + default: + printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") + } + return [resultVar] + } + ) + } // MARK: - ExportSwift @@ -261,71 +387,7 @@ struct IntrinsicJSFragment: Sendable { return .swiftHeapObjectLowerParameter case .void: return .void case .optional(let wrappedType): - // Pre-compute the wrapped type fragment outside the closure - let wrappedFragment = try lowerParameter(type: wrappedType) - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, scope, printer, cleanupCode in - let value = arguments[0] - let isNullVar = scope.variable("isNull") - let isSomeVar = scope.variable("isSome") - let resultVars = scope.variable("results") - - printer.write("const \(isNullVar) = (\(value) === null || \(value) === undefined);") - printer.write("const \(isSomeVar) = \(isNullVar) ? 0 : 1;") - printer.write("let \(resultVars) = [\(isSomeVar)];") - - // Handle different wrapped types with proper variable scoping - switch wrappedType { - case .string: - // Pre-declare variables for string handling - let bytesVar = scope.variable("\(value)Bytes") - let idVar = scope.variable("\(value)Id") - printer.write("let \(idVar);") - - printer.write("if (!\(isNullVar)) {") - printer.indent { - printer.write("const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));") - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") - printer.write("\(resultVars).push(\(idVar));") - printer.write("\(resultVars).push(\(bytesVar).length);") - } - printer.write("} else {") - printer.indent { - printer.write("\(resultVars).push(0);") // dummy bytes ID - printer.write("\(resultVars).push(0);") // dummy length - } - printer.write("}") - - // Add conditional cleanup - cleanupCode.write("if (\(idVar) !== undefined) {") - cleanupCode.write(" \(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") - cleanupCode.write("}") - - default: - // For other non-string types, use the original delegation approach - printer.write("if (!\(isNullVar)) {") - printer.indent { - let wrappedResults = wrappedFragment.printCode([value], scope, printer, cleanupCode) - for result in wrappedResults { - printer.write("\(resultVars).push(\(result));") - } - } - printer.write("} else {") - printer.indent { - switch wrappedType { - case .int, .bool, .float, .double: - printer.write("\(resultVars).push(0);") - default: - printer.write("\(resultVars).push(0);") - } - } - printer.write("}") - } - - return ["...\(resultVars)"] - } - ) + return try .optionalLowerParameter(wrappedType: wrappedType) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -339,7 +401,7 @@ struct IntrinsicJSFragment: Sendable { throw BridgeJSLinkError(message: "Namespace enums are not supported to be passed as parameters: \(string)") } } - + /// Returns a fragment that lifts a Wasm core value to a JS value for return values static func liftReturn(type: BridgeType) throws -> IntrinsicJSFragment { switch type { @@ -349,43 +411,7 @@ struct IntrinsicJSFragment: Sendable { case .jsObject: return .jsObjectLiftReturn case .swiftHeapObject(let name): return .swiftHeapObjectLiftReturn(name) case .void: return .void - case .optional(let wrappedType): - // For ExportSwift, optional returns use type-specific storage and return void - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, scope, printer, cleanupCode in - let resultVar = scope.variable("optResult") - - switch wrappedType { - case .bool: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") - case .int: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") - case .float: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") - case .double: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") - case .string: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") - case .swiftHeapObject(let className): - let pointerVar = scope.variable("pointer") - printer.write("const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;") - printer.write("const \(resultVar) = \(pointerVar) === null ? null : \(className).__construct(\(pointerVar));") - default: - // Fallback for unsupported optional types - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") - } - - return [resultVar] - } - ) + case .optional(let wrappedType): return .optionalLiftReturn(wrappedType: wrappedType) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -422,7 +448,6 @@ struct IntrinsicJSFragment: Sendable { message: "Void can't appear in parameters of imported JS functions" ) case .optional(let wrappedType): - // Pre-compute the wrapped type fragment outside the closure let wrappedFragment = try liftParameter(type: wrappedType) return IntrinsicJSFragment( parameters: ["isSome"], @@ -437,7 +462,6 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - // For non-null case, lift the wrapped type parameters let liftedResults = wrappedFragment.printCode([], scope, printer, cleanupCode) if let firstResult = liftedResults.first { @@ -484,7 +508,6 @@ struct IntrinsicJSFragment: Sendable { ) case .void: return .void case .optional(let wrappedType): - // Pre-compute the wrapped type fragment outside the closure let wrappedFragment = try lowerReturn(type: wrappedType) return IntrinsicJSFragment( parameters: ["value"], @@ -500,7 +523,6 @@ struct IntrinsicJSFragment: Sendable { printer.write("if (!\(isNullVar)) {") printer.indent { - // For non-null values, get the lowered wrapped type values let wrappedResults = wrappedFragment.printCode([value], scope, printer, cleanupCode) for result in wrappedResults { printer.write("\(resultVars).push(\(result));") @@ -508,7 +530,6 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - // For null values, push dummy values based on wrapped type let dummyResults = wrappedFragment.printCode(["null"], scope, printer, cleanupCode) for result in dummyResults { printer.write("\(resultVars).push(\(result));") diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift index 419800d6..55040c70 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift @@ -13,6 +13,9 @@ enum APIResult { @JS func roundtripAPIResult(result: APIResult) -> APIResult { return result } +@JS func roundTripOptionalAPIResult(result: APIResult?) -> APIResult? { + return result +} @JS enum ComplexResult { @@ -29,6 +32,9 @@ enum ComplexResult { @JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult { return result } +@JS func roundTripOptionalComplexResult(result: ComplexResult?) -> ComplexResult? { + return result +} @JS enum Utilities { @@ -39,8 +45,16 @@ enum Utilities { } } +@JS func roundTripOptionalUtilitiesResult(result: Utilities.Result?) -> Utilities.Result? { + return result +} + @JS(namespace: "API") @JS enum NetworkingResult { case success(String) case failure(String, Int) } + +@JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult? { + return result +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumCase.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumCase.swift index 15bdb9f4..c54ee6ae 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumCase.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumCase.swift @@ -14,6 +14,7 @@ @JS func setDirection(_ direction: Direction) @JS func getDirection() -> Direction @JS func processDirection(_ input: Direction) -> Status +@JS func roundTripOptionalDirection(_ input: Direction?) -> Direction? @JS(enumStyle: .tsEnum) enum TSDirection { case north @@ -24,6 +25,7 @@ @JS func setTSDirection(_ direction: TSDirection) @JS func getTSDirection() -> TSDirection +@JS func roundTripOptionalTSDirection(_ input: TSDirection?) -> TSDirection? @JS public enum PublicStatus { case success diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumRawType.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumRawType.swift index 799df164..7e2c17ac 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumRawType.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumRawType.swift @@ -75,42 +75,51 @@ @JS func setTheme(_ theme: Theme) @JS func getTheme() -> Theme +@JS func roundTripOptionalTheme(_ input: Theme?) -> Theme? @JS func setTSTheme(_ theme: TSTheme) @JS func getTSTheme() -> TSTheme +@JS func roundTripOptionalTSTheme(_ input: TSTheme?) -> TSTheme? @JS func setFeatureFlag(_ flag: FeatureFlag) @JS func getFeatureFlag() -> FeatureFlag +@JS func roundTripOptionalFeatureFlag(_ input: FeatureFlag?) -> FeatureFlag? @JS func setHttpStatus(_ status: HttpStatus) @JS func getHttpStatus() -> HttpStatus +@JS func roundTripOptionalHttpStatus(_ input: HttpStatus?) -> HttpStatus? @JS func setTSHttpStatus(_ status: TSHttpStatus) @JS func getTSHttpStatus() -> TSHttpStatus +@JS func roundTripOptionalHttpStatus(_ input: TSHttpStatus?) -> TSHttpStatus? @JS func setPriority(_ priority: Priority) @JS func getPriority() -> Priority +@JS func roundTripOptionalPriority(_ input: Priority?) -> Priority? @JS func setFileSize(_ size: FileSize) @JS func getFileSize() -> FileSize +@JS func roundTripOptionalFileSize(_ input: FileSize?) -> FileSize? @JS func setUserId(_ id: UserId) @JS func getUserId() -> UserId +@JS func roundTripOptionalUserId(_ input: UserId?) -> UserId? @JS func setTokenId(_ token: TokenId) @JS func getTokenId() -> TokenId +@JS func roundTripOptionalTokenId(_ input: TokenId?) -> TokenId? @JS func setSessionId(_ session: SessionId) @JS func getSessionId() -> SessionId +@JS func roundTripOptionalSessionId(_ input: SessionId?) -> SessionId? @JS func setPrecision(_ precision: Precision) @JS func getPrecision() -> Precision +@JS func roundTripOptionalPrecision(_ input: Precision?) -> Precision? @JS func setRatio(_ ratio: Ratio) @JS func getRatio() -> Ratio - -@JS func setFeatureFlag(_ featureFlag: FeatureFlag) -@JS func getFeatureFlag() -> FeatureFlag +@JS func roundTripOptionalRatio(_ input: Ratio?) -> Ratio? @JS func processTheme(_ theme: Theme) -> HttpStatus @JS func convertPriority(_ status: HttpStatus) -> Priority diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts index d2855a1a..358a9308 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts @@ -1,15 +1,14 @@ -export function testOptionalNumber(value: number | null): void; -export function testOptionalString(value: string | undefined): void; -export function testOptionalBool(value: boolean | null | undefined): void; -export function testOptionalReturn(): string | null; -export function testOptionalNumberReturn(): number | undefined; -export function testMixedOptionals(required: string, optional: number | null): boolean | undefined; +export function roundTripOptionalNumber(value: number | null): number | null; +export function roundTripOptionalString(value: string | null): string | null; +export function roundTripOptionalBool(value: boolean | null): boolean | null; +export function roundTripOptionalClass(value: TestClass | null): TestClass | null; +export function testMixedOptionals(required: string, optional: number | null): boolean | null; export class TestClass { optionalProperty: string | null; - constructor(param: number | undefined); + constructor(param: number | null); methodWithOptional(value: boolean | null): void; - methodReturningOptional(): string | undefined; -} \ No newline at end of file + methodReturningOptional(): string | null; +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts index 18d8a763..584336e2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts @@ -62,9 +62,13 @@ export type Exports = { handle(result: APIResult): void; getResult(): APIResult; roundtripAPIResult(result: APIResult): APIResult; + roundTripOptionalAPIResult(result: APIResult | null): APIResult | null; handleComplex(result: ComplexResult): void; getComplexResult(): ComplexResult; roundtripComplexResult(result: ComplexResult): ComplexResult; + roundTripOptionalComplexResult(result: ComplexResult | null): ComplexResult | null; + roundTripOptionalUtilitiesResult(result: Utilities.Result | null): Utilities.Result | null; + roundTripOptionalNetworkingResult(result: NetworkingResult | null): NetworkingResult | null; } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index eca4f3c7..d0557cf2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -529,6 +529,25 @@ export async function createInstantiator(options, swift) { if (resultCleanup) { resultCleanup(); } return ret; }, + roundTripOptionalAPIResult: function bjs_roundTripOptionalAPIResult(result) { + const isSome = result != null; + let resultCaseId, resultCleanup; + if (isSome) { + const enumResult = enumHelpers.APIResult.lower(result); + resultCaseId = enumResult.caseId; + resultCleanup = enumResult.cleanup; + } + instance.exports.bjs_roundTripOptionalAPIResult(+isSome, isSome ? resultCaseId : 0); + const isNull = (tmpRetTag === -1); + let optResult; + if (isNull) { + optResult = null; + } else { + optResult = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + } + if (resultCleanup) { resultCleanup(); } + return optResult; + }, handleComplex: function bjs_handleComplex(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.ComplexResult.lower(result); instance.exports.bjs_handleComplex(resultCaseId); @@ -546,6 +565,63 @@ export async function createInstantiator(options, swift) { if (resultCleanup) { resultCleanup(); } return ret; }, + roundTripOptionalComplexResult: function bjs_roundTripOptionalComplexResult(result) { + const isSome = result != null; + let resultCaseId, resultCleanup; + if (isSome) { + const enumResult = enumHelpers.ComplexResult.lower(result); + resultCaseId = enumResult.caseId; + resultCleanup = enumResult.cleanup; + } + instance.exports.bjs_roundTripOptionalComplexResult(+isSome, isSome ? resultCaseId : 0); + const isNull = (tmpRetTag === -1); + let optResult; + if (isNull) { + optResult = null; + } else { + optResult = enumHelpers.ComplexResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + } + if (resultCleanup) { resultCleanup(); } + return optResult; + }, + roundTripOptionalUtilitiesResult: function bjs_roundTripOptionalUtilitiesResult(result) { + const isSome = result != null; + let resultCaseId, resultCleanup; + if (isSome) { + const enumResult = enumHelpers.Result.lower(result); + resultCaseId = enumResult.caseId; + resultCleanup = enumResult.cleanup; + } + instance.exports.bjs_roundTripOptionalUtilitiesResult(+isSome, isSome ? resultCaseId : 0); + const isNull = (tmpRetTag === -1); + let optResult; + if (isNull) { + optResult = null; + } else { + optResult = enumHelpers.Result.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + } + if (resultCleanup) { resultCleanup(); } + return optResult; + }, + roundTripOptionalNetworkingResult: function bjs_roundTripOptionalNetworkingResult(result) { + const isSome = result != null; + let resultCaseId, resultCleanup; + if (isSome) { + const enumResult = enumHelpers.NetworkingResult.lower(result); + resultCaseId = enumResult.caseId; + resultCleanup = enumResult.cleanup; + } + instance.exports.bjs_roundTripOptionalNetworkingResult(+isSome, isSome ? resultCaseId : 0); + const isNull = (tmpRetTag === -1); + let optResult; + if (isNull) { + optResult = null; + } else { + optResult = enumHelpers.NetworkingResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + } + if (resultCleanup) { resultCleanup(); } + return optResult; + }, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts index 1375cc36..9a04f59a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts @@ -35,8 +35,10 @@ export type Exports = { setDirection(direction: Direction): void; getDirection(): Direction; processDirection(input: Direction): Status; + roundTripOptionalDirection(input: Direction | null): Direction | null; setTSDirection(direction: TSDirection): void; getTSDirection(): TSDirection; + roundTripOptionalTSDirection(input: TSDirection | null): TSDirection | null; } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js index fadc678a..5eb0b420 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js @@ -186,6 +186,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_processDirection(input); return ret; }, + roundTripOptionalDirection: function bjs_roundTripOptionalDirection(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalDirection(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setTSDirection: function bjs_setTSDirection(direction) { instance.exports.bjs_setTSDirection(direction); }, @@ -193,6 +200,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getTSDirection(); return ret; }, + roundTripOptionalTSDirection: function bjs_roundTripOptionalTSDirection(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalTSDirection(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts index 51b020ad..96297187 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts @@ -92,30 +92,40 @@ export type Ratio = typeof Ratio[keyof typeof Ratio]; export type Exports = { setTheme(theme: Theme): void; getTheme(): Theme; + roundTripOptionalTheme(input: Theme | null): Theme | null; setTSTheme(theme: TSTheme): void; getTSTheme(): TSTheme; + roundTripOptionalTSTheme(input: TSTheme | null): TSTheme | null; setFeatureFlag(flag: FeatureFlag): void; getFeatureFlag(): FeatureFlag; + roundTripOptionalFeatureFlag(input: FeatureFlag | null): FeatureFlag | null; setHttpStatus(status: HttpStatus): void; getHttpStatus(): HttpStatus; + roundTripOptionalHttpStatus(input: HttpStatus | null): HttpStatus | null; setTSHttpStatus(status: TSHttpStatus): void; getTSHttpStatus(): TSHttpStatus; + roundTripOptionalHttpStatus(input: TSHttpStatus | null): TSHttpStatus | null; setPriority(priority: Priority): void; getPriority(): Priority; + roundTripOptionalPriority(input: Priority | null): Priority | null; setFileSize(size: FileSize): void; getFileSize(): FileSize; + roundTripOptionalFileSize(input: FileSize | null): FileSize | null; setUserId(id: UserId): void; getUserId(): UserId; + roundTripOptionalUserId(input: UserId | null): UserId | null; setTokenId(token: TokenId): void; getTokenId(): TokenId; + roundTripOptionalTokenId(input: TokenId | null): TokenId | null; setSessionId(session: SessionId): void; getSessionId(): SessionId; + roundTripOptionalSessionId(input: SessionId | null): SessionId | null; setPrecision(precision: Precision): void; getPrecision(): Precision; + roundTripOptionalPrecision(input: Precision | null): Precision | null; setRatio(ratio: Ratio): void; getRatio(): Ratio; - setFeatureFlag(featureFlag: FeatureFlag): void; - getFeatureFlag(): FeatureFlag; + roundTripOptionalRatio(input: Ratio | null): Ratio | null; processTheme(theme: Theme): HttpStatus; convertPriority(status: HttpStatus): Priority; validateSession(session: SessionId): Theme; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js index a1b5dcf3..fe1b4258 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js @@ -238,6 +238,21 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; }, + roundTripOptionalTheme: function bjs_roundTripOptionalTheme(input) { + const isSome = input != null; + let inputId, inputBytes; + if (isSome) { + inputBytes = textEncoder.encode(input); + inputId = swift.memory.retain(inputBytes); + } + instance.exports.bjs_roundTripOptionalTheme(+isSome, isSome ? inputId : 0, isSome ? inputBytes.length : 0); + const optResult = tmpRetString; + tmpRetString = undefined; + if (inputId != undefined) { + swift.memory.release(inputId); + } + return optResult; + }, setTSTheme: function bjs_setTSTheme(theme) { const themeBytes = textEncoder.encode(theme); const themeId = swift.memory.retain(themeBytes); @@ -250,6 +265,21 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; }, + roundTripOptionalTSTheme: function bjs_roundTripOptionalTSTheme(input) { + const isSome = input != null; + let inputId, inputBytes; + if (isSome) { + inputBytes = textEncoder.encode(input); + inputId = swift.memory.retain(inputBytes); + } + instance.exports.bjs_roundTripOptionalTSTheme(+isSome, isSome ? inputId : 0, isSome ? inputBytes.length : 0); + const optResult = tmpRetString; + tmpRetString = undefined; + if (inputId != undefined) { + swift.memory.release(inputId); + } + return optResult; + }, setFeatureFlag: function bjs_setFeatureFlag(flag) { instance.exports.bjs_setFeatureFlag(flag); }, @@ -257,6 +287,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getFeatureFlag(); return ret !== 0; }, + roundTripOptionalFeatureFlag: function bjs_roundTripOptionalFeatureFlag(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalFeatureFlag(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalBool; + tmpRetOptionalBool = undefined; + return optResult; + }, setHttpStatus: function bjs_setHttpStatus(status) { instance.exports.bjs_setHttpStatus(status); }, @@ -264,6 +301,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getHttpStatus(); return ret; }, + roundTripOptionalHttpStatus: function bjs_roundTripOptionalHttpStatus(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalHttpStatus(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setTSHttpStatus: function bjs_setTSHttpStatus(status) { instance.exports.bjs_setTSHttpStatus(status); }, @@ -271,6 +315,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getTSHttpStatus(); return ret; }, + roundTripOptionalHttpStatus: function bjs_roundTripOptionalHttpStatus(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalHttpStatus(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setPriority: function bjs_setPriority(priority) { instance.exports.bjs_setPriority(priority); }, @@ -278,6 +329,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getPriority(); return ret; }, + roundTripOptionalPriority: function bjs_roundTripOptionalPriority(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalPriority(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setFileSize: function bjs_setFileSize(size) { instance.exports.bjs_setFileSize(size); }, @@ -285,6 +343,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getFileSize(); return ret; }, + roundTripOptionalFileSize: function bjs_roundTripOptionalFileSize(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalFileSize(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setUserId: function bjs_setUserId(id) { instance.exports.bjs_setUserId(id); }, @@ -292,6 +357,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getUserId(); return ret; }, + roundTripOptionalUserId: function bjs_roundTripOptionalUserId(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalUserId(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setTokenId: function bjs_setTokenId(token) { instance.exports.bjs_setTokenId(token); }, @@ -299,6 +371,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getTokenId(); return ret; }, + roundTripOptionalTokenId: function bjs_roundTripOptionalTokenId(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalTokenId(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setSessionId: function bjs_setSessionId(session) { instance.exports.bjs_setSessionId(session); }, @@ -306,6 +385,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getSessionId(); return ret; }, + roundTripOptionalSessionId: function bjs_roundTripOptionalSessionId(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalSessionId(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return optResult; + }, setPrecision: function bjs_setPrecision(precision) { instance.exports.bjs_setPrecision(precision); }, @@ -313,6 +399,13 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getPrecision(); return ret; }, + roundTripOptionalPrecision: function bjs_roundTripOptionalPrecision(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalPrecision(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return optResult; + }, setRatio: function bjs_setRatio(ratio) { instance.exports.bjs_setRatio(ratio); }, @@ -320,12 +413,12 @@ export async function createInstantiator(options, swift) { const ret = instance.exports.bjs_getRatio(); return ret; }, - setFeatureFlag: function bjs_setFeatureFlag(featureFlag) { - instance.exports.bjs_setFeatureFlag(featureFlag); - }, - getFeatureFlag: function bjs_getFeatureFlag() { - const ret = instance.exports.bjs_getFeatureFlag(); - return ret !== 0; + roundTripOptionalRatio: function bjs_roundTripOptionalRatio(input) { + const isSome = input != null; + instance.exports.bjs_roundTripOptionalRatio(+isSome, isSome ? input : 0); + const optResult = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return optResult; }, processTheme: function bjs_processTheme(theme) { const themeBytes = textEncoder.encode(theme); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js index 5a9508af..d397efaa 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js @@ -200,21 +200,14 @@ export async function createInstantiator(options, swift) { return ret; } changeName(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_Greeter_changeName(this.pointer, ...results); - if (nameId !== undefined) { + instance.exports.bjs_Greeter_changeName(this.pointer, +isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + if (nameId != undefined) { swift.memory.release(nameId); } } @@ -225,21 +218,14 @@ export async function createInstantiator(options, swift) { return optResult; } set name(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let valueId; - if (!isNull) { - const valueBytes = textEncoder.encode(value); + const isSome = value != null; + let valueId, valueBytes; + if (isSome) { + valueBytes = textEncoder.encode(value); valueId = swift.memory.retain(valueBytes); - results.push(valueId); - results.push(valueBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_Greeter_name_set(this.pointer, ...results); - if (valueId !== undefined) { + instance.exports.bjs_Greeter_name_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + if (valueId != undefined) { swift.memory.release(valueId); } } @@ -260,21 +246,14 @@ export async function createInstantiator(options, swift) { return optResult; } set optionalName(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let valueId; - if (!isNull) { - const valueBytes = textEncoder.encode(value); + const isSome = value != null; + let valueId, valueBytes; + if (isSome) { + valueBytes = textEncoder.encode(value); valueId = swift.memory.retain(valueBytes); - results.push(valueId); - results.push(valueBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_OptionalPropertyHolder_optionalName_set(this.pointer, ...results); - if (valueId !== undefined) { + instance.exports.bjs_OptionalPropertyHolder_optionalName_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + if (valueId != undefined) { swift.memory.release(valueId); } } @@ -285,15 +264,8 @@ export async function createInstantiator(options, swift) { return optResult; } set optionalAge(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(value); - } else { - results.push(0); - } - instance.exports.bjs_OptionalPropertyHolder_optionalAge_set(this.pointer, ...results); + const isSome = value != null; + instance.exports.bjs_OptionalPropertyHolder_optionalAge_set(this.pointer, +isSome, isSome ? value : 0); } get optionalGreeter() { instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_get(this.pointer); @@ -303,278 +275,166 @@ export async function createInstantiator(options, swift) { return optResult; } set optionalGreeter(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(value.pointer); - } else { - results.push(0); - } - instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_set(this.pointer, ...results); + const isSome = value != null; + instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_set(this.pointer, +isSome, isSome ? value.pointer : 0); } } return { Greeter, OptionalPropertyHolder, roundTripOptionalClass: function bjs_roundTripOptionalClass(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(value.pointer); - } else { - results.push(0); - } - instance.exports.bjs_roundTripOptionalClass(...results); + const isSome = value != null; + instance.exports.bjs_roundTripOptionalClass(+isSome, isSome ? value.pointer : 0); const pointer = tmpRetOptionalHeapObject; tmpRetOptionalHeapObject = undefined; const optResult = pointer === null ? null : Greeter.__construct(pointer); return optResult; }, testOptionalPropertyRoundtrip: function bjs_testOptionalPropertyRoundtrip(holder) { - const isNull = (holder === null || holder === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(holder.pointer); - } else { - results.push(0); - } - instance.exports.bjs_testOptionalPropertyRoundtrip(...results); + const isSome = holder != null; + instance.exports.bjs_testOptionalPropertyRoundtrip(+isSome, isSome ? holder.pointer : 0); const pointer = tmpRetOptionalHeapObject; tmpRetOptionalHeapObject = undefined; const optResult = pointer === null ? null : OptionalPropertyHolder.__construct(pointer); return optResult; }, roundTripString: function bjs_roundTripString(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_roundTripString(...results); + instance.exports.bjs_roundTripString(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (nameId !== undefined) { + if (nameId != undefined) { swift.memory.release(nameId); } return optResult; }, roundTripInt: function bjs_roundTripInt(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(value); - } else { - results.push(0); - } - instance.exports.bjs_roundTripInt(...results); + const isSome = value != null; + instance.exports.bjs_roundTripInt(+isSome, isSome ? value : 0); const optResult = tmpRetOptionalInt; tmpRetOptionalInt = undefined; return optResult; }, roundTripBool: function bjs_roundTripBool(flag) { - const isNull = (flag === null || flag === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(flag); - } else { - results.push(0); - } - instance.exports.bjs_roundTripBool(...results); + const isSome = flag != null; + instance.exports.bjs_roundTripBool(+isSome, isSome ? flag : 0); const optResult = tmpRetOptionalBool; tmpRetOptionalBool = undefined; return optResult; }, roundTripFloat: function bjs_roundTripFloat(number) { - const isNull = (number === null || number === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(number); - } else { - results.push(0); - } - instance.exports.bjs_roundTripFloat(...results); + const isSome = number != null; + instance.exports.bjs_roundTripFloat(+isSome, isSome ? number : 0); const optResult = tmpRetOptionalFloat; tmpRetOptionalFloat = undefined; return optResult; }, roundTripDouble: function bjs_roundTripDouble(precision) { - const isNull = (precision === null || precision === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(precision); - } else { - results.push(0); - } - instance.exports.bjs_roundTripDouble(...results); + const isSome = precision != null; + instance.exports.bjs_roundTripDouble(+isSome, isSome ? precision : 0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; return optResult; }, roundTripSyntax: function bjs_roundTripSyntax(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_roundTripSyntax(...results); + instance.exports.bjs_roundTripSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (nameId !== undefined) { + if (nameId != undefined) { swift.memory.release(nameId); } return optResult; }, roundTripMixSyntax: function bjs_roundTripMixSyntax(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_roundTripMixSyntax(...results); + instance.exports.bjs_roundTripMixSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (nameId !== undefined) { + if (nameId != undefined) { swift.memory.release(nameId); } return optResult; }, roundTripSwiftSyntax: function bjs_roundTripSwiftSyntax(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_roundTripSwiftSyntax(...results); + instance.exports.bjs_roundTripSwiftSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (nameId !== undefined) { + if (nameId != undefined) { swift.memory.release(nameId); } return optResult; }, roundTripMixedSwiftSyntax: function bjs_roundTripMixedSwiftSyntax(name) { - const isNull = (name === null || name === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let nameId; - if (!isNull) { - const nameBytes = textEncoder.encode(name); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); nameId = swift.memory.retain(nameBytes); - results.push(nameId); - results.push(nameBytes.length); - } else { - results.push(0); - results.push(0); } - instance.exports.bjs_roundTripMixedSwiftSyntax(...results); + instance.exports.bjs_roundTripMixedSwiftSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (nameId !== undefined) { + if (nameId != undefined) { swift.memory.release(nameId); } return optResult; }, roundTripWithSpaces: function bjs_roundTripWithSpaces(value) { - const isNull = (value === null || value === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(value); - } else { - results.push(0); - } - instance.exports.bjs_roundTripWithSpaces(...results); + const isSome = value != null; + instance.exports.bjs_roundTripWithSpaces(+isSome, isSome ? value : 0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; return optResult; }, roundTripAlias: function bjs_roundTripAlias(age) { - const isNull = (age === null || age === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - if (!isNull) { - results.push(age); - } else { - results.push(0); - } - instance.exports.bjs_roundTripAlias(...results); + const isSome = age != null; + instance.exports.bjs_roundTripAlias(+isSome, isSome ? age : 0); const optResult = tmpRetOptionalInt; tmpRetOptionalInt = undefined; return optResult; }, testMixedOptionals: function bjs_testMixedOptionals(firstName, lastName) { - const isNull = (firstName === null || firstName === undefined); - const isSome = isNull ? 0 : 1; - let results = [isSome]; - let firstNameId; - if (!isNull) { - const firstNameBytes = textEncoder.encode(firstName); + const isSome = firstName != null; + let firstNameId, firstNameBytes; + if (isSome) { + firstNameBytes = textEncoder.encode(firstName); firstNameId = swift.memory.retain(firstNameBytes); - results.push(firstNameId); - results.push(firstNameBytes.length); - } else { - results.push(0); - results.push(0); } - const isNull1 = (lastName === null || lastName === undefined); - const isSome1 = isNull1 ? 0 : 1; - let results1 = [isSome1]; - let lastNameId; - if (!isNull1) { - const lastNameBytes = textEncoder.encode(lastName); + const isSome1 = lastName != null; + let lastNameId, lastNameBytes; + if (isSome1) { + lastNameBytes = textEncoder.encode(lastName); lastNameId = swift.memory.retain(lastNameBytes); - results1.push(lastNameId); - results1.push(lastNameBytes.length); - } else { - results1.push(0); - results1.push(0); } - instance.exports.bjs_testMixedOptionals(...results, ...results1); + instance.exports.bjs_testMixedOptionals(+isSome, isSome ? firstNameId : 0, isSome ? firstNameBytes.length : 0, +isSome1, isSome1 ? lastNameId : 0, isSome1 ? lastNameBytes.length : 0); const optResult = tmpRetString; tmpRetString = undefined; - if (firstNameId !== undefined) { + if (firstNameId != undefined) { swift.memory.release(firstNameId); } - if (lastNameId !== undefined) { + if (lastNameId != undefined) { swift.memory.release(lastNameId); } return optResult; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts index e54856d7..6fb85b8c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts @@ -12,11 +12,10 @@ export interface TestClass { export type Exports = { } export type Imports = { - testOptionalNumber(value: any): void; - testOptionalString(value: any): void; - testOptionalBool(value: any): void; - testOptionalReturn(): any; - testOptionalNumberReturn(): any; + roundTripOptionalNumber(value: any): any; + roundTripOptionalString(value: any): any; + roundTripOptionalBool(value: any): any; + roundTripOptionalClass(value: any): any; testMixedOptionals(required: string, optional: any): any; TestClass: { new(param: any): TestClass; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js index 288b3f1d..37b338e0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js @@ -139,39 +139,36 @@ export async function createInstantiator(options, swift) { } } const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; - TestModule["bjs_testOptionalNumber"] = function bjs_testOptionalNumber(value) { + TestModule["bjs_roundTripOptionalNumber"] = function bjs_roundTripOptionalNumber(value) { try { - imports.testOptionalNumber(swift.memory.getObject(value)); - } catch (error) { - setException(error); - } - } - TestModule["bjs_testOptionalString"] = function bjs_testOptionalString(value) { - try { - imports.testOptionalString(swift.memory.getObject(value)); + let ret = imports.roundTripOptionalNumber(swift.memory.getObject(value)); + return swift.memory.retain(ret); } catch (error) { setException(error); + return 0 } } - TestModule["bjs_testOptionalBool"] = function bjs_testOptionalBool(value) { + TestModule["bjs_roundTripOptionalString"] = function bjs_roundTripOptionalString(value) { try { - imports.testOptionalBool(swift.memory.getObject(value)); + let ret = imports.roundTripOptionalString(swift.memory.getObject(value)); + return swift.memory.retain(ret); } catch (error) { setException(error); + return 0 } } - TestModule["bjs_testOptionalReturn"] = function bjs_testOptionalReturn() { + TestModule["bjs_roundTripOptionalBool"] = function bjs_roundTripOptionalBool(value) { try { - let ret = imports.testOptionalReturn(); + let ret = imports.roundTripOptionalBool(swift.memory.getObject(value)); return swift.memory.retain(ret); } catch (error) { setException(error); return 0 } } - TestModule["bjs_testOptionalNumberReturn"] = function bjs_testOptionalNumberReturn() { + TestModule["bjs_roundTripOptionalClass"] = function bjs_roundTripOptionalClass(value) { try { - let ret = imports.testOptionalNumberReturn(); + let ret = imports.roundTripOptionalClass(swift.memory.getObject(value)); return swift.memory.retain(ret); } catch (error) { setException(error); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json index 37f4b61f..72dc8b44 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -414,6 +414,38 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalAPIResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalAPIResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + } + }, { "abiName" : "bjs_handleComplex", "effects" : { @@ -477,6 +509,102 @@ "_0" : "ComplexResult" } } + }, + { + "abiName" : "bjs_roundTripOptionalComplexResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalComplexResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalUtilitiesResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalUtilitiesResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "Utilities.Result" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "Utilities.Result" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalNetworkingResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalNetworkingResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "NetworkingResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "NetworkingResult" + } + } + } + } } ], "moduleName" : "TestModule" diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift index 410c569d..fd414ece 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift @@ -6,8 +6,8 @@ @_spi(BridgeJS) import JavaScriptKit -private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { +extension APIResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -26,7 +26,7 @@ private extension APIResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -52,8 +52,8 @@ private extension APIResult { } } -private extension ComplexResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { +extension ComplexResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -72,7 +72,7 @@ private extension ComplexResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -126,8 +126,8 @@ private extension ComplexResult { } } -private extension Utilities.Result { - static func bridgeJSLiftParameter(_ caseId: Int32) -> Utilities.Result { +extension Utilities.Result: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> Utilities.Result { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -140,7 +140,7 @@ private extension Utilities.Result { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -167,8 +167,8 @@ private extension Utilities.Result { } } -private extension NetworkingResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> NetworkingResult { +extension NetworkingResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> NetworkingResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -179,7 +179,7 @@ private extension NetworkingResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -230,6 +230,17 @@ public func _bjs_roundtripAPIResult(result: Int32) -> Void { #endif } +@_expose(wasm, "bjs_roundTripOptionalAPIResult") +@_cdecl("bjs_roundTripOptionalAPIResult") +public func _bjs_roundTripOptionalAPIResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalAPIResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_handleComplex") @_cdecl("bjs_handleComplex") public func _bjs_handleComplex(result: Int32) -> Void { @@ -260,4 +271,37 @@ public func _bjs_roundtripComplexResult(result: Int32) -> Void { #else fatalError("Only available on WebAssembly") #endif +} + +@_expose(wasm, "bjs_roundTripOptionalComplexResult") +@_cdecl("bjs_roundTripOptionalComplexResult") +public func _bjs_roundTripOptionalComplexResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalComplexResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalUtilitiesResult") +@_cdecl("bjs_roundTripOptionalUtilitiesResult") +public func _bjs_roundTripOptionalUtilitiesResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalUtilitiesResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalNetworkingResult") +@_cdecl("bjs_roundTripOptionalNetworkingResult") +public func _bjs_roundTripOptionalNetworkingResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalNetworkingResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + 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/EnumCase.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json index b90bd40b..38b26020 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json @@ -170,6 +170,38 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalDirection", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalDirection", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Direction" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Direction" + } + } + } + } + }, { "abiName" : "bjs_setTSDirection", "effects" : { @@ -209,6 +241,38 @@ "_0" : "TSDirection" } } + }, + { + "abiName" : "bjs_roundTripOptionalTSDirection", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTSDirection", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "TSDirection" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "TSDirection" + } + } + } + } } ], "moduleName" : "TestModule" diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.swift index e222a2bb..4a1be41e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -extension Direction { +extension Direction: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -49,7 +49,7 @@ extension Direction { } } -extension Status { +extension Status: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -88,7 +88,7 @@ extension Status { } } -extension TSDirection { +extension TSDirection: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -131,7 +131,7 @@ extension TSDirection { } } -extension PublicStatus { +extension PublicStatus: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -194,6 +194,17 @@ public func _bjs_processDirection(input: Int32) -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalDirection") +@_cdecl("bjs_roundTripOptionalDirection") +public func _bjs_roundTripOptionalDirection(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalDirection(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setTSDirection") @_cdecl("bjs_setTSDirection") public func _bjs_setTSDirection(direction: Int32) -> Void { @@ -213,4 +224,15 @@ public func _bjs_getTSDirection() -> Int32 { #else fatalError("Only available on WebAssembly") #endif +} + +@_expose(wasm, "bjs_roundTripOptionalTSDirection") +@_cdecl("bjs_roundTripOptionalTSDirection") +public func _bjs_roundTripOptionalTSDirection(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTSDirection(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + 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 23120ea7..daa5577a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -extension Networking.API.Method { +extension Networking.API.Method: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -55,7 +55,7 @@ extension Configuration.LogLevel: _BridgedSwiftEnumNoPayload { extension Configuration.Port: _BridgedSwiftEnumNoPayload { } -extension Internal.SupportedMethod { +extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json index 09ce5d6e..c6f17c0a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json @@ -416,6 +416,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalTheme", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTheme", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } + } + }, { "abiName" : "bjs_setTSTheme", "effects" : { @@ -458,6 +492,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalTSTheme", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTSTheme", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" + } + } + } + } + }, { "abiName" : "bjs_setFeatureFlag", "effects" : { @@ -500,6 +568,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalFeatureFlag", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalFeatureFlag", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "Bool" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "FeatureFlag", + "_1" : "Bool" + } + } + } + } + }, { "abiName" : "bjs_setHttpStatus", "effects" : { @@ -542,6 +644,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalHttpStatus", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalHttpStatus", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" + } + } + } + } + }, { "abiName" : "bjs_setTSHttpStatus", "effects" : { @@ -584,6 +720,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalHttpStatus", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalHttpStatus", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSHttpStatus", + "_1" : "Int" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSHttpStatus", + "_1" : "Int" + } + } + } + } + }, { "abiName" : "bjs_setPriority", "effects" : { @@ -626,6 +796,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalPriority", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalPriority", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Priority", + "_1" : "Int32" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Priority", + "_1" : "Int32" + } + } + } + } + }, { "abiName" : "bjs_setFileSize", "effects" : { @@ -668,6 +872,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalFileSize", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalFileSize", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "FileSize", + "_1" : "Int64" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "FileSize", + "_1" : "Int64" + } + } + } + } + }, { "abiName" : "bjs_setUserId", "effects" : { @@ -710,6 +948,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalUserId", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalUserId", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "UserId", + "_1" : "UInt" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "UserId", + "_1" : "UInt" + } + } + } + } + }, { "abiName" : "bjs_setTokenId", "effects" : { @@ -752,6 +1024,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalTokenId", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTokenId", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TokenId", + "_1" : "UInt32" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TokenId", + "_1" : "UInt32" + } + } + } + } + }, { "abiName" : "bjs_setSessionId", "effects" : { @@ -794,6 +1100,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalSessionId", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalSessionId", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "SessionId", + "_1" : "UInt64" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "SessionId", + "_1" : "UInt64" + } + } + } + } + }, { "abiName" : "bjs_setPrecision", "effects" : { @@ -836,6 +1176,40 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalPrecision", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalPrecision", + "parameters" : [ + { + "label" : "_", + "name" : "input", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Precision", + "_1" : "Float" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Precision", + "_1" : "Float" + } + } + } + } + }, { "abiName" : "bjs_setRatio", "effects" : { @@ -879,44 +1253,36 @@ } }, { - "abiName" : "bjs_setFeatureFlag", + "abiName" : "bjs_roundTripOptionalRatio", "effects" : { "isAsync" : false, "isThrows" : false }, - "name" : "setFeatureFlag", + "name" : "roundTripOptionalRatio", "parameters" : [ { "label" : "_", - "name" : "featureFlag", + "name" : "input", "type" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "Bool" + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Ratio", + "_1" : "Double" + } + } } } } ], "returnType" : { - "void" : { - - } - } - }, - { - "abiName" : "bjs_getFeatureFlag", - "effects" : { - "isAsync" : false, - "isThrows" : false - }, - "name" : "getFeatureFlag", - "parameters" : [ - - ], - "returnType" : { - "rawValueEnum" : { - "_0" : "FeatureFlag", - "_1" : "Bool" + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Ratio", + "_1" : "Double" + } + } } } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.swift index e0a32e84..9e03a665 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.swift @@ -63,6 +63,17 @@ public func _bjs_getTheme() -> Void { #endif } +@_expose(wasm, "bjs_roundTripOptionalTheme") +@_cdecl("bjs_roundTripOptionalTheme") +public func _bjs_roundTripOptionalTheme(inputIsSome: Int32, inputBytes: Int32, inputLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTheme(_: Optional.bridgeJSLiftParameter(inputIsSome, inputBytes, inputLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setTSTheme") @_cdecl("bjs_setTSTheme") public func _bjs_setTSTheme(themeBytes: Int32, themeLength: Int32) -> Void { @@ -84,6 +95,17 @@ public func _bjs_getTSTheme() -> Void { #endif } +@_expose(wasm, "bjs_roundTripOptionalTSTheme") +@_cdecl("bjs_roundTripOptionalTSTheme") +public func _bjs_roundTripOptionalTSTheme(inputIsSome: Int32, inputBytes: Int32, inputLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTSTheme(_: Optional.bridgeJSLiftParameter(inputIsSome, inputBytes, inputLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setFeatureFlag") @_cdecl("bjs_setFeatureFlag") public func _bjs_setFeatureFlag(flag: Int32) -> Void { @@ -105,6 +127,17 @@ public func _bjs_getFeatureFlag() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalFeatureFlag") +@_cdecl("bjs_roundTripOptionalFeatureFlag") +public func _bjs_roundTripOptionalFeatureFlag(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalFeatureFlag(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setHttpStatus") @_cdecl("bjs_setHttpStatus") public func _bjs_setHttpStatus(status: Int32) -> Void { @@ -126,6 +159,17 @@ public func _bjs_getHttpStatus() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalHttpStatus") +@_cdecl("bjs_roundTripOptionalHttpStatus") +public func _bjs_roundTripOptionalHttpStatus(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalHttpStatus(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setTSHttpStatus") @_cdecl("bjs_setTSHttpStatus") public func _bjs_setTSHttpStatus(status: Int32) -> Void { @@ -147,6 +191,17 @@ public func _bjs_getTSHttpStatus() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalHttpStatus") +@_cdecl("bjs_roundTripOptionalHttpStatus") +public func _bjs_roundTripOptionalHttpStatus(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalHttpStatus(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setPriority") @_cdecl("bjs_setPriority") public func _bjs_setPriority(priority: Int32) -> Void { @@ -168,6 +223,17 @@ public func _bjs_getPriority() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalPriority") +@_cdecl("bjs_roundTripOptionalPriority") +public func _bjs_roundTripOptionalPriority(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalPriority(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setFileSize") @_cdecl("bjs_setFileSize") public func _bjs_setFileSize(size: Int32) -> Void { @@ -189,6 +255,17 @@ public func _bjs_getFileSize() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalFileSize") +@_cdecl("bjs_roundTripOptionalFileSize") +public func _bjs_roundTripOptionalFileSize(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalFileSize(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setUserId") @_cdecl("bjs_setUserId") public func _bjs_setUserId(id: Int32) -> Void { @@ -210,6 +287,17 @@ public func _bjs_getUserId() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalUserId") +@_cdecl("bjs_roundTripOptionalUserId") +public func _bjs_roundTripOptionalUserId(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalUserId(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setTokenId") @_cdecl("bjs_setTokenId") public func _bjs_setTokenId(token: Int32) -> Void { @@ -231,6 +319,17 @@ public func _bjs_getTokenId() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalTokenId") +@_cdecl("bjs_roundTripOptionalTokenId") +public func _bjs_roundTripOptionalTokenId(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTokenId(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setSessionId") @_cdecl("bjs_setSessionId") public func _bjs_setSessionId(session: Int32) -> Void { @@ -252,6 +351,17 @@ public func _bjs_getSessionId() -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalSessionId") +@_cdecl("bjs_roundTripOptionalSessionId") +public func _bjs_roundTripOptionalSessionId(inputIsSome: Int32, inputValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalSessionId(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setPrecision") @_cdecl("bjs_setPrecision") public func _bjs_setPrecision(precision: Float32) -> Void { @@ -273,6 +383,17 @@ public func _bjs_getPrecision() -> Float32 { #endif } +@_expose(wasm, "bjs_roundTripOptionalPrecision") +@_cdecl("bjs_roundTripOptionalPrecision") +public func _bjs_roundTripOptionalPrecision(inputIsSome: Int32, inputValue: Float32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalPrecision(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_setRatio") @_cdecl("bjs_setRatio") public func _bjs_setRatio(ratio: Float64) -> Void { @@ -294,21 +415,11 @@ public func _bjs_getRatio() -> Float64 { #endif } -@_expose(wasm, "bjs_setFeatureFlag") -@_cdecl("bjs_setFeatureFlag") -public func _bjs_setFeatureFlag(featureFlag: Int32) -> Void { - #if arch(wasm32) - setFeatureFlag(_: FeatureFlag.bridgeJSLiftParameter(featureFlag)) - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_getFeatureFlag") -@_cdecl("bjs_getFeatureFlag") -public func _bjs_getFeatureFlag() -> Int32 { +@_expose(wasm, "bjs_roundTripOptionalRatio") +@_cdecl("bjs_roundTripOptionalRatio") +public func _bjs_roundTripOptionalRatio(inputIsSome: Int32, inputValue: Float64) -> Void { #if arch(wasm32) - let ret = getFeatureFlag() + let ret = roundTripOptionalRatio(_: Optional.bridgeJSLiftParameter(inputIsSome, inputValue)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift index 6c8ef5b9..e660592c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift @@ -6,77 +6,64 @@ @_spi(BridgeJS) import JavaScriptKit -func testOptionalNumber(_ value: JSObject) throws(JSException) -> Void { +func roundTripOptionalNumber(_ value: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testOptionalNumber") - func bjs_testOptionalNumber(_ value: Int32) -> Void + @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalNumber") + func bjs_roundTripOptionalNumber(_ value: Int32) -> Int32 #else - func bjs_testOptionalNumber(_ value: Int32) -> Void { + func bjs_roundTripOptionalNumber(_ value: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif - bjs_testOptionalNumber(value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } -} - -func testOptionalString(_ value: JSObject) throws(JSException) -> Void { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testOptionalString") - func bjs_testOptionalString(_ value: Int32) -> Void - #else - func bjs_testOptionalString(_ value: Int32) -> Void { - fatalError("Only available on WebAssembly") - } - #endif - bjs_testOptionalString(value.bridgeJSLowerParameter()) + let ret = bjs_roundTripOptionalNumber(value.bridgeJSLowerParameter()) if let error = _swift_js_take_exception() { throw error } + return JSObject.bridgeJSLiftReturn(ret) } -func testOptionalBool(_ value: JSObject) throws(JSException) -> Void { +func roundTripOptionalString(_ value: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testOptionalBool") - func bjs_testOptionalBool(_ value: Int32) -> Void + @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalString") + func bjs_roundTripOptionalString(_ value: Int32) -> Int32 #else - func bjs_testOptionalBool(_ value: Int32) -> Void { + func bjs_roundTripOptionalString(_ value: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif - bjs_testOptionalBool(value.bridgeJSLowerParameter()) + let ret = bjs_roundTripOptionalString(value.bridgeJSLowerParameter()) if let error = _swift_js_take_exception() { throw error } + return JSObject.bridgeJSLiftReturn(ret) } -func testOptionalReturn() throws(JSException) -> JSObject { +func roundTripOptionalBool(_ value: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testOptionalReturn") - func bjs_testOptionalReturn() -> Int32 + @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalBool") + func bjs_roundTripOptionalBool(_ value: Int32) -> Int32 #else - func bjs_testOptionalReturn() -> Int32 { + func bjs_roundTripOptionalBool(_ value: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif - let ret = bjs_testOptionalReturn() + let ret = bjs_roundTripOptionalBool(value.bridgeJSLowerParameter()) if let error = _swift_js_take_exception() { throw error } return JSObject.bridgeJSLiftReturn(ret) } -func testOptionalNumberReturn() throws(JSException) -> JSObject { +func roundTripOptionalClass(_ value: JSObject) throws(JSException) -> JSObject { #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testOptionalNumberReturn") - func bjs_testOptionalNumberReturn() -> Int32 + @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalClass") + func bjs_roundTripOptionalClass(_ value: Int32) -> Int32 #else - func bjs_testOptionalNumberReturn() -> Int32 { + func bjs_roundTripOptionalClass(_ value: Int32) -> Int32 { fatalError("Only available on WebAssembly") } #endif - let ret = bjs_testOptionalNumberReturn() + let ret = bjs_roundTripOptionalClass(value.bridgeJSLowerParameter()) if let error = _swift_js_take_exception() { throw error } diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index d7026ac0..5a664c48 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -306,6 +306,28 @@ extension _JSBridgedClass { /// The conformance is automatically synthesized by the BridgeJS code generator. public protocol _BridgedSwiftEnumNoPayload {} +/// A protocol that Swift case enum types (enums without raw values or associated values) conform to. +/// +/// The conformance is automatically synthesized by the BridgeJS code generator. +/// Case enums are lowered to Int32 values representing their case index. +public protocol _BridgedSwiftCaseEnum { + // MARK: ImportTS & ExportSwift + @_spi(BridgeJS) consuming func bridgeJSLowerParameter() -> Int32 + @_spi(BridgeJS) static func bridgeJSLiftReturn(_ value: Int32) -> Self + @_spi(BridgeJS) static func bridgeJSLiftParameter(_ value: Int32) -> Self + @_spi(BridgeJS) consuming func bridgeJSLowerReturn() -> Int32 +} + +/// A protocol that Swift associated value enum types conform to. +/// +/// The conformance is automatically synthesized by the BridgeJS code generator. +/// Associated value enums use a stack-based ABI for complex data transfer. +public protocol _BridgedSwiftAssociatedValueEnum { + // MARK: ImportTS & ExportSwift + @_spi(BridgeJS) static func bridgeJSLiftParameter(_ caseId: Int32) -> Self + @_spi(BridgeJS) consuming func bridgeJSLowerReturn() -> Void +} + extension _BridgedSwiftEnumNoPayload where Self: RawRepresentable, RawValue == String { // MARK: ImportTS @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Int32 { rawValue.bridgeJSLowerParameter() } @@ -430,7 +452,7 @@ extension Optional where Wrapped == Bool { @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { switch self { case .none: - return (0, 0) // isSome=0, dummy value=0 + return (0, 0) case .some(let value): return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value } @@ -530,7 +552,7 @@ extension Optional where Wrapped == String { @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Int32 { switch self { case .none: - return 0 // Return special sentinel value for nil string + return 0 case .some(let value): return value.bridgeJSLowerParameter() } @@ -765,3 +787,248 @@ extension Optional where Wrapped == Double { } } } + +/// Optional support for case enums +extension Optional where Wrapped: _BridgedSwiftCaseEnum { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { + switch self { + case .none: + return (0, 0) // isSome=0, dummy value=0 + case .some(let value): + return (1, value.bridgeJSLowerParameter()) // isSome=1, actual enum value + } + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + if isSome == 0 { + return nil + } else { + return Wrapped.bridgeJSLiftReturn(wrappedValue) + } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + if isSome == 0 { + return nil + } else { + return Wrapped.bridgeJSLiftParameter(wrappedValue) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_return_optional_int") + func _swift_js_return_optional_int(_ isSome: Int32, _ value: Int32) + #else + /// Sets the optional int for return value storage + func _swift_js_return_optional_int(_ isSome: Int32, _ value: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_return_optional_int(0, 0) + case .some(let value): + _swift_js_return_optional_int(1, value.bridgeJSLowerReturn()) + } + } +} + +public protocol _BridgedSwiftTypeLoweredIntoVoidType { + // MARK: ExportSwift + consuming func bridgeJSLowerReturn() -> Void +} + +extension Optional where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + switch consume self { + case .none: + () + case .some(let value): + value.bridgeJSLowerReturn() + } + } +} + +// MARK: Optional Raw Value Enum Support + +/// Optional support for String raw value enums (like Theme: String) +extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == String { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Int32 { + let optionalRawValue: String? = self?.rawValue + return optionalRawValue.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> Wrapped? { + let optionalRawValue = String?.bridgeJSLiftReturn(isSome) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ bytes: Int32, _ count: Int32) -> Wrapped? { + let optionalRawValue = String?.bridgeJSLiftParameter(isSome, bytes, count) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + let optionalRawValue: String? = self?.rawValue + optionalRawValue.bridgeJSLowerReturn() + } +} + +/// Optional support for Int raw value enums +extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Int { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { + let optionalRawValue: Int? = self?.rawValue + return optionalRawValue.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + let optionalRawValue = Int?.bridgeJSLiftReturn(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + let optionalRawValue = Int?.bridgeJSLiftParameter(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + let optionalRawValue: Int? = self?.rawValue + optionalRawValue.bridgeJSLowerReturn() + } +} + +/// Optional support for Bool raw value enums +extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Bool { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { + let optionalRawValue: Bool? = self?.rawValue + return optionalRawValue.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + let optionalRawValue = Bool?.bridgeJSLiftReturn(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { + let optionalRawValue = Bool?.bridgeJSLiftParameter(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + let optionalRawValue: Bool? = self?.rawValue + optionalRawValue.bridgeJSLowerReturn() + } +} + +/// Optional support for Float raw value enums +extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Float { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float32) { + let optionalRawValue: Float? = self?.rawValue + return optionalRawValue.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float32) -> Wrapped? { + let optionalRawValue = Float?.bridgeJSLiftReturn(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Wrapped? { + let optionalRawValue = Float?.bridgeJSLiftParameter(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + let optionalRawValue: Float? = self?.rawValue + optionalRawValue.bridgeJSLowerReturn() + } +} + +/// Optional support for Double raw value enums +extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Double { + // MARK: ImportTS + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float64) { + let optionalRawValue: Double? = self?.rawValue + return optionalRawValue.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float64) -> Wrapped? { + let optionalRawValue = Double?.bridgeJSLiftReturn(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Wrapped? { + let optionalRawValue = Double?.bridgeJSLiftParameter(isSome, wrappedValue) + return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + let optionalRawValue: Double? = self?.rawValue + optionalRawValue.bridgeJSLowerReturn() + } +} + +// MARK: Optional Associated Value Enum Support + +/// Optional support for associated value enums +extension Optional where Wrapped: _BridgedSwiftAssociatedValueEnum { + // MARK: ImportTS + @available(*, unavailable, message: "Optional associated value enums are not supported to be passed to imported JS functions") + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional associated value enums are not supported to be returned from imported JS functions") + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ caseId: Int32) -> Wrapped? { return nil } + + // MARK: ExportSwift + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Wrapped? { + if isSome == 0 { + return nil + } else { + return Wrapped.bridgeJSLiftParameter(caseId) + } + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + #if arch(wasm32) + @_extern(wasm, module: "bjs", name: "swift_js_push_tag") + func _swift_js_push_tag(_ tag: Int32) + #else + /// Pushes an enum tag to the return tag storage + func _swift_js_push_tag(_ tag: Int32) { + _onlyAvailableOnWasm() + } + #endif + + switch consume self { + case .none: + _swift_js_push_tag(-1) // Use -1 as sentinel for null + case .some(let value): + value.bridgeJSLowerReturn() // This will push to tmpRetTag and stacks + } + } +} diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md index 7c512982..c19b8c52 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md @@ -66,4 +66,5 @@ This command will: - - - +- - diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Optional.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Optional.md new file mode 100644 index 00000000..24b04d74 --- /dev/null +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Optional.md @@ -0,0 +1,127 @@ +# Exporting Swift Optionals to JS + +Learn how to use Swift optionals in functions, classes, and enums exported to JavaScript. + +## Overview + +> Tip: You can quickly preview what interfaces will be exposed on the Swift/TypeScript sides using the [BridgeJS Playground](https://swiftwasm.org/JavaScriptKit/PlayBridgeJS/). + +BridgeJS provides comprehensive support for Swift optionals across all bridged types. When you use `Optional` or `T?` in Swift, it automatically maps to `T | null` in TypeScript/JavaScript. + +Swift optionals are translated to union types with `null` (not `undefined`) because `null` represents an explicit "no value" state that aligns semantically with Swift's `nil`. This design choice ensures consistent null handling across the Swift-JavaScript bridge and avoids the ambiguity that comes with JavaScript's `undefined`. + +> Important: BridgeJS optional support is **Swift → JavaScript only**. TypeScript → Swift optional imports are not currently supported. + +## Supported Optional Syntax + +BridgeJS recognizes all Swift optional syntax variants: + +```swift +// All of these work identically +@JS func test(name: String?) -> String? { return name } +@JS func test(name: Optional) -> Optional { return name } +@JS func test(name: Swift.Optional) -> Swift.Optional { return name } +@JS func test(name: Swift.Optional< String >) -> Swift.Optional< String. > { return name } + +// Type aliases work too +typealias OptionalName = String? +@JS func test(name: OptionalName) -> OptionalName { return name } +``` + +## Optional Parameters + +All parameter types can be made optional, including primitives, objects, and enums: + +```swift +@JS public func processOptionalData( + text: String?, // Optional string + count: Int?, // Optional integer + flag: Bool?, // Optional boolean + rate: Double?, // Optional double + user: User? // Optional Swift class +) -> String +``` + +Generated TypeScript type: + +```typescript +export type Exports = { + processOptionalData( + text: string | null, + count: number | null, + flag: boolean | null, + rate: number | null, + user: User | null + ): string; +} +``` + +## Optional Return Values + +Functions can return optional values of any supported type: + +```swift +@JS public func processText(input: String?) -> String? +``` + +Generated TypeScript type: + +```typescript +export type Exports = { + processText( + input: string | null + ): string | null; +} +``` + +## Optionals in classes + +Class properties can be optional with proper getter/setter support. +Constructors can accept optional parameters. + +```swift +@JS public class UserProfile { + @JS public var name: String? + @JS public var email: String? + @JS public var age: Int? + @JS public var avatar: User? + + @JS public init(name: String?) + @JS public func updateProfile(email: String?, age: Int?) +} + +``` + +Generated TypeScript definition: + +```typescript +export interface UserProfile extends SwiftHeapObject { + name: string | null; + email: string | null; + age: number | null; + avatar: User | null; + + updateProfile(email: string | null, age: number | null): void; +} +export type UserProfile = { + UserProfile: { + new(name: string | null): UserProfile; + } +} +``` + +## Supported Features + +| Swift Optional Feature | Status | +|:----------------------|:-------| +| Optional primitive parameters: `Int?`, `String?`, etc. | ✅ | +| Optional primitive return values | ✅ | +| Optional object parameters: `MyClass?`, `JSObject?` | ✅ | +| Optional object return values | ✅ | +| Optional enum type parameters and returns | ✅ | +| Optional properties within associated value enum cases | ✅ | +| Optional properties in classes | ✅ | +| Optional constructor parameters | ✅ | +| Type aliases for optionals | ✅ | +| All optional syntax: `T?`, `Optional`, `Swift.Optional` | ✅ | +| Nested optionals: `T??` | ❌ | \ No newline at end of file diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md index 71b92573..668fe21b 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Supported-Types.md @@ -11,8 +11,8 @@ Use this page as a quick reference for how common TypeScript types appear in Swi | `boolean` | `Bool` | | TODO | `Array` | | TODO | `Dictionary` | -| TODO | `Optional` | | `T \| undefined` | TODO | +| `T \| null` | `Optional` | | `Promise` | `JSPromise` | | `any` / `unknown` / `object` | `JSObject` | | Other types | `JSObject` | diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index e6271e00..571e6ffe 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -478,10 +478,41 @@ typealias OptionalAge = Int? return age } -@JS func roundTripOptionalClass(value: Greeter?) -> Greeter? { +@JS func roundTripOptionalStatus(value: Status?) -> Status? { + return value +} + +@JS func roundTripOptionalTheme(value: Theme?) -> Theme? { + return value +} + +@JS func roundTripOptionalHttpStatus(value: HttpStatus?) -> HttpStatus? { + return value +} + +@JS func roundTripOptionalTSDirection(value: TSDirection?) -> TSDirection? { + return value +} + +@JS func roundTripOptionalTSTheme(value: TSTheme?) -> TSTheme? { return value } +@JS func roundTripOptionalNetworkingAPIMethod(_ method: Networking.API.Method?) -> Networking.API.Method? { + return method +} + +@JS func roundTripOptionalAPIResult(value: APIResult?) -> APIResult? { + return value +} + +@JS func roundTripOptionalComplexResult(_ result: ComplexResult?) -> ComplexResult? { + return result +} + +@JS func roundTripOptionalClass(value: Greeter?) -> Greeter? { + return value +} @JS class OptionalPropertyHolder { @JS var optionalName: String? @JS var optionalAge: Int? = nil diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 41ba24a6..e18c0cbd 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -6,7 +6,7 @@ @_spi(BridgeJS) import JavaScriptKit -extension Direction { +extension Direction: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -49,7 +49,7 @@ extension Direction { } } -extension Status { +extension Status: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -94,7 +94,7 @@ extension Theme: _BridgedSwiftEnumNoPayload { extension HttpStatus: _BridgedSwiftEnumNoPayload { } -extension TSDirection { +extension TSDirection: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -140,7 +140,7 @@ extension TSDirection { extension TSTheme: _BridgedSwiftEnumNoPayload { } -extension Networking.API.Method { +extension Networking.API.Method: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -189,7 +189,7 @@ extension Configuration.LogLevel: _BridgedSwiftEnumNoPayload { extension Configuration.Port: _BridgedSwiftEnumNoPayload { } -extension Internal.SupportedMethod { +extension Internal.SupportedMethod: _BridgedSwiftCaseEnum { @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { return bridgeJSRawValue } @@ -224,8 +224,8 @@ extension Internal.SupportedMethod { } } -private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { +extension APIResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -244,7 +244,7 @@ private extension APIResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -270,8 +270,8 @@ private extension APIResult { } } -private extension ComplexResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { +extension ComplexResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -292,7 +292,7 @@ private extension ComplexResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -354,8 +354,8 @@ private extension ComplexResult { } } -private extension Utilities.Result { - static func bridgeJSLiftParameter(_ caseId: Int32) -> Utilities.Result { +extension Utilities.Result: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> Utilities.Result { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -368,7 +368,7 @@ private extension Utilities.Result { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -395,8 +395,8 @@ private extension Utilities.Result { } } -private extension API.NetworkingResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> API.NetworkingResult { +extension API.NetworkingResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> API.NetworkingResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -407,7 +407,7 @@ private extension API.NetworkingResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -1420,6 +1420,94 @@ public func _bjs_roundTripOptionalTypeAlias(ageIsSome: Int32, ageValue: Int32) - #endif } +@_expose(wasm, "bjs_roundTripOptionalStatus") +@_cdecl("bjs_roundTripOptionalStatus") +public func _bjs_roundTripOptionalStatus(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalStatus(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalTheme") +@_cdecl("bjs_roundTripOptionalTheme") +public func _bjs_roundTripOptionalTheme(valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTheme(value: Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalHttpStatus") +@_cdecl("bjs_roundTripOptionalHttpStatus") +public func _bjs_roundTripOptionalHttpStatus(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalHttpStatus(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalTSDirection") +@_cdecl("bjs_roundTripOptionalTSDirection") +public func _bjs_roundTripOptionalTSDirection(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTSDirection(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalTSTheme") +@_cdecl("bjs_roundTripOptionalTSTheme") +public func _bjs_roundTripOptionalTSTheme(valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalTSTheme(value: Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalNetworkingAPIMethod") +@_cdecl("bjs_roundTripOptionalNetworkingAPIMethod") +public func _bjs_roundTripOptionalNetworkingAPIMethod(methodIsSome: Int32, methodValue: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalNetworkingAPIMethod(_: Optional.bridgeJSLiftParameter(methodIsSome, methodValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalAPIResult") +@_cdecl("bjs_roundTripOptionalAPIResult") +public func _bjs_roundTripOptionalAPIResult(valueIsSome: Int32, valueCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalAPIResult(value: Optional.bridgeJSLiftParameter(valueIsSome, valueCaseId)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOptionalComplexResult") +@_cdecl("bjs_roundTripOptionalComplexResult") +public func _bjs_roundTripOptionalComplexResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalComplexResult(_: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripOptionalClass") @_cdecl("bjs_roundTripOptionalClass") public func _bjs_roundTripOptionalClass(valueIsSome: Int32, valueValue: UnsafeMutableRawPointer) -> Void { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index b52d7811..e1c5be24 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -3396,6 +3396,268 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalStatus", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalStatus", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Status" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Status" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalTheme", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTheme", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalHttpStatus", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalHttpStatus", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "HttpStatus", + "_1" : "Int" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalTSDirection", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTSDirection", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "TSDirection" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "TSDirection" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalTSTheme", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalTSTheme", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "rawValueEnum" : { + "_0" : "TSTheme", + "_1" : "String" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalNetworkingAPIMethod", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalNetworkingAPIMethod", + "parameters" : [ + { + "label" : "_", + "name" : "method", + "type" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Networking.API.Method" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "caseEnum" : { + "_0" : "Networking.API.Method" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalAPIResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalAPIResult", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + } + }, + { + "abiName" : "bjs_roundTripOptionalComplexResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalComplexResult", + "parameters" : [ + { + "label" : "_", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "ComplexResult" + } + } + } + } + }, { "abiName" : "bjs_roundTripOptionalClass", "effects" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 1065be50..f491bcf2 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -484,9 +484,16 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.roundTripOptionalSwiftSyntax("Swift"), "Swift"); assert.equal(exports.roundTripOptionalWithSpaces(null), null); assert.equal(exports.roundTripOptionalWithSpaces(1.618), 1.618); - assert.equal(exports.roundTripOptionalTypeAlias(null), null); assert.equal(exports.roundTripOptionalTypeAlias(25), 25); + assert.equal(exports.roundTripOptionalStatus(Status.Success), Status.Success); + assert.equal(exports.roundTripOptionalTheme(Theme.Light), Theme.Light); + assert.equal(exports.roundTripOptionalHttpStatus(HttpStatus.Ok), HttpStatus.Ok); + assert.equal(exports.roundTripOptionalTSDirection(TSDirection.North), TSDirection.North); + assert.equal(exports.roundTripOptionalTSTheme(TSTheme.Light), TSTheme.Light); + assert.equal(exports.roundTripOptionalNetworkingAPIMethod(globalThis.Networking.API.Method.Get), globalThis.Networking.API.Method.Get); + assert.deepEqual(exports.roundTripOptionalAPIResult(p1), p1); + assert.deepEqual(exports.roundTripOptionalComplexResult(cl1), cl1); const optionalGreeter = new exports.Greeter("Schrödinger"); const optionalGreeter2 = exports.roundTripOptionalClass(optionalGreeter); From 6091dfc54eaa7392ae4105b0e56cb946c5cf6ce7 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Mon, 8 Sep 2025 12:15:59 +0200 Subject: [PATCH 3/5] BridgeJS: Support optionals in associated value enum parameters + cleanup --- .../Sources/BridgeJSCore/ExportSwift.swift | 171 ++++--- .../BridgeJSCore/TypeDeclResolver.swift | 4 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 10 +- .../Sources/BridgeJSLink/JSGlueGen.swift | 189 +++++--- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 2 +- .../TS2Skeleton/JavaScript/src/index.d.ts | 1 - .../Inputs/EnumAssociatedValue.swift | 10 + .../Inputs/OptionalUnionTypes.d.ts | 14 - ...tionalParameters.swift => Optionals.swift} | 19 +- .../EnumAssociatedValue.Export.d.ts | 12 + .../EnumAssociatedValue.Export.js | 159 +++++++ .../OptionalUnionTypes.Import.d.ts | 30 -- .../OptionalUnionTypes.Import.js | 246 ---------- ...ters.Export.d.ts => Optionals.Export.d.ts} | 5 +- ...rameters.Export.js => Optionals.Export.js} | 34 +- .../ExportSwiftTests/EnumAssociatedValue.json | 120 +++++ .../EnumAssociatedValue.swift | 73 +++ ...OptionalParameters.json => Optionals.json} | 60 ++- ...tionalParameters.swift => Optionals.swift} | 19 +- .../ImportTSTests/OptionalUnionTypes.swift | 176 ------- .../JavaScriptKit/BridgeJSInstrincics.swift | 439 +++++++++--------- .../BridgeJSRuntimeTests/ExportAPITests.swift | 14 +- .../Generated/BridgeJS.ExportSwift.swift | 73 +++ .../JavaScript/BridgeJS.ExportSwift.json | 120 +++++ Tests/prelude.mjs | 25 +- 25 files changed, 1153 insertions(+), 872 deletions(-) delete mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts rename Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/{OptionalParameters.swift => Optionals.swift} (80%) delete mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts delete mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js rename Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/{OptionalParameters.Export.d.ts => Optionals.Export.d.ts} (92%) rename Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/{OptionalParameters.Export.js => Optionals.Export.js} (93%) rename Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/{OptionalParameters.json => Optionals.json} (91%) rename Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/{OptionalParameters.swift => Optionals.swift} (93%) delete mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 3966415a..e44178a8 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -142,7 +142,7 @@ public class ExportSwift { hint: "Only primitive types and types defined in the same module are allowed" ) } - + private func diagnoseNestedOptional(node: some SyntaxProtocol, type: String) { diagnose( node: node, @@ -192,17 +192,17 @@ public class ExportSwift { var parameters: [Parameter] = [] for param in node.signature.parameterClause.parameters { let resolvedType = self.parent.lookupType(for: param.type) - + if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription) continue } - + guard let type = resolvedType else { diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription) continue } - + let name = param.secondName?.text ?? param.firstName.text let label = param.firstName.text parameters.append(Parameter(label: label, name: name, type: type)) @@ -210,12 +210,12 @@ public class ExportSwift { let returnType: BridgeType if let returnClause = node.signature.returnClause { let resolvedType = self.parent.lookupType(for: returnClause.type) - + if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription) return nil } - + guard let type = resolvedType else { diagnoseUnsupportedType(node: returnClause.type, type: returnClause.type.trimmedDescription) return nil @@ -561,12 +561,24 @@ public class ExportSwift { switch associatedValue.type { case .string, .int, .float, .double, .bool: break + case .optional(let wrappedType): + switch wrappedType { + case .string, .int, .float, .double, .bool: + break + default: + diagnose( + node: node, + message: "Unsupported associated value type: \(associatedValue.type.swiftType)", + hint: + "Only primitive types and optional primitives (String?, Int?, Float?, Double?, Bool?) are supported in associated-value enums" + ) + } 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" + "Only primitive types and optional primitives (String?, Int?, Float?, Double?, Bool?) are supported in associated-value enums" ) } } @@ -744,22 +756,24 @@ public class ExportSwift { } // Optional if let identifierType = type.as(IdentifierTypeSyntax.self), - identifierType.name.text == "Optional", - let genericArgs = identifierType.genericArgumentClause?.arguments, - genericArgs.count == 1, - let argType = genericArgs.first?.argument { + identifierType.name.text == "Optional", + let genericArgs = identifierType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = genericArgs.first?.argument + { if let baseType = lookupType(for: argType) { return .optional(baseType) } } // Swift.Optional if let memberType = type.as(MemberTypeSyntax.self), - let baseType = memberType.baseType.as(IdentifierTypeSyntax.self), - baseType.name.text == "Swift", - memberType.name.text == "Optional", - let genericArgs = memberType.genericArgumentClause?.arguments, - genericArgs.count == 1, - let argType = genericArgs.first?.argument { + let baseType = memberType.baseType.as(IdentifierTypeSyntax.self), + baseType.name.text == "Swift", + memberType.name.text == "Optional", + let genericArgs = memberType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = genericArgs.first?.argument + { if let wrappedType = lookupType(for: argType) { return .optional(wrappedType) } @@ -771,7 +785,7 @@ public class ExportSwift { } let typeName = type.trimmedDescription - if let primitiveType = BridgeType.primitive(swiftType: typeName) { + if let primitiveType = BridgeType(swiftType: typeName) { return primitiveType } @@ -890,7 +904,7 @@ public class ExportSwift { } else { argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter } } - + let typeNameForIntrinsic: String switch param.type { case .optional(let wrappedType): @@ -898,7 +912,7 @@ public class ExportSwift { default: typeNameForIntrinsic = param.type.swiftType } - + liftedParameterExprs.append( ExprSyntax( "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" @@ -996,11 +1010,6 @@ public class ExportSwift { return } - if case .optional(_) = returnType { - append("return ret.bridgeJSLowerReturn()") - return - } - append("return ret.bridgeJSLowerReturn()") } @@ -1158,8 +1167,26 @@ public class ExportSwift { return "\(paramName)Float.bridgeJSLiftParameter(_swift_js_pop_param_f32())" case .double: return "\(paramName)Double.bridgeJSLiftParameter(_swift_js_pop_param_f64())" - case .optional(_): - return "nil" + case .optional(let wrappedType): + switch wrappedType { + case .string: + return + "\(paramName)Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())" + case .int: + return + "\(paramName)Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())" + case .bool: + return + "\(paramName)Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())" + case .float: + return + "\(paramName)Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_f32())" + case .double: + return + "\(paramName)Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_f64())" + default: + return "" + } default: return "\(paramName)Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())" } @@ -1196,6 +1223,30 @@ public class ExportSwift { bodyLines.append("_swift_js_push_f32(\(paramName))") case .double: bodyLines.append("_swift_js_push_f64(\(paramName))") + case .optional(let wrappedType): + bodyLines.append("let __bjs_isSome_\(paramName) = \(paramName) != nil") + bodyLines.append("if let __bjs_unwrapped_\(paramName) = \(paramName) {") + switch wrappedType { + case .string: + bodyLines.append("var __bjs_str_\(paramName) = __bjs_unwrapped_\(paramName)") + bodyLines.append("__bjs_str_\(paramName).withUTF8 { ptr in") + bodyLines.append("_swift_js_push_string(ptr.baseAddress, Int32(ptr.count))") + bodyLines.append("}") + case .int: + bodyLines.append("_swift_js_push_int(Int32(__bjs_unwrapped_\(paramName)))") + case .bool: + bodyLines.append("_swift_js_push_int(__bjs_unwrapped_\(paramName) ? 1 : 0)") + case .float: + bodyLines.append("_swift_js_push_f32(__bjs_unwrapped_\(paramName))") + case .double: + bodyLines.append("_swift_js_push_f64(__bjs_unwrapped_\(paramName))") + default: + bodyLines.append( + "preconditionFailure(\"BridgeJS: unsupported optional wrapped type in generated code\")" + ) + } + bodyLines.append("}") + bodyLines.append("_swift_js_push_int(__bjs_isSome_\(paramName) ? 1 : 0)") default: bodyLines.append( "preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")" @@ -1406,55 +1457,24 @@ extension AttributeListSyntax { } extension BridgeType { - // This initializer is primarily for string-based type descriptions, - // especially for internal and raw type lookups. - // For full SwiftSyntax based parsing, `ExportSwift.lookupType(for:)` should be used. init?(swiftType: String) { - // Use SwiftSyntax to parse the string into a TypeSyntax for robust optional detection - let typeSyntax = TypeSyntax(stringLiteral: swiftType) - - // 1. Handle T? syntax (OptionalTypeSyntax) - if let optionalType = typeSyntax.as(OptionalTypeSyntax.self) { - let wrappedTypeString = optionalType.wrappedType.trimmedDescription - if let baseType = BridgeType(swiftType: wrappedTypeString) { - self = .optional(baseType) - return - } - } - - // 2. Handle Optional syntax (IdentifierTypeSyntax with generic arguments) - if let identifierType = typeSyntax.as(IdentifierTypeSyntax.self), - identifierType.name.text == "Optional", - let genericArgs = identifierType.genericArgumentClause?.arguments, - genericArgs.count == 1, - let argType = genericArgs.first?.argument { - let innerTypeString = argType.trimmedDescription - if let baseType = BridgeType(swiftType: innerTypeString) { - self = .optional(baseType) - return - } - } - - // Fallback to primitive type handling if not an optional - if let primitive = BridgeType.primitive(swiftType: swiftType) { - self = primitive - return - } - - return nil - } - - // Helper for direct primitive type mapping (used by init? and elsewhere) - fileprivate static func primitive(swiftType: String) -> BridgeType? { switch swiftType { - case "Int": return .int - case "Float": return .float - case "Double": return .double - case "String": return .string - case "Bool": return .bool - case "Void": return .void - case "JSObject": return .jsObject(nil) - default: return nil + case "Int": + self = .int + case "Float": + self = .float + case "Double": + self = .double + case "String": + self = .string + case "Bool": + self = .bool + case "Void": + self = .void + case "JSObject": + self = .jsObject(nil) + default: + return nil } } } @@ -1507,7 +1527,6 @@ extension BridgeType { ("caseId", .i32) ]) } - func liftParameterInfo() throws -> LiftingIntrinsicInfo { switch self { @@ -1589,7 +1608,7 @@ extension BridgeType { case .associatedValueEnum: return .associatedValueEnum case .namespaceEnum: - throw BridgeJSCoreError("Namespace enums are not supported as return types") + throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift index 282837c6..33e36a4a 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift @@ -63,7 +63,7 @@ class TypeDeclResolver { override func visitPost(_ node: EnumDeclSyntax) { visitPostNominalDecl() } - + override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { let name = node.name.text let qualifiedName = scope.map(\.name.text) + [name] @@ -139,7 +139,7 @@ class TypeDeclResolver { } return nil } - + /// Resolves a type usage node to a type alias declaration /// /// - Parameter type: The SwiftSyntax node representing a type appearance in source code. diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 31717fed..9715014c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -365,7 +365,9 @@ struct BridgeJSLink { } printer.write("} else {") printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = Math.fround(value);") + printer.write( + "\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = Math.fround(value);" + ) } printer.write("}") } @@ -393,7 +395,7 @@ struct BridgeJSLink { printer.indent { printer.write(lines: [ "const bytes = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, ptr, len);", - "\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);" + "\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);", ]) } printer.write("}") @@ -407,7 +409,9 @@ struct BridgeJSLink { } printer.write("} else {") printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedSwift).memory.getObject(objectId);") + printer.write( + "\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedSwift).memory.getObject(objectId);" + ) } printer.write("}") } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 883dfe96..ad07774d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -248,7 +248,7 @@ struct IntrinsicJSFragment: Sendable { } ) } - + static func optionalLowerParameter(wrappedType: BridgeType) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["value"], @@ -256,12 +256,12 @@ struct IntrinsicJSFragment: Sendable { let value = arguments[0] let isSomeVar = scope.variable("isSome") printer.write("const \(isSomeVar) = \(value) != null;") - + switch wrappedType { case .string, .rawValueEnum(_, .string): let bytesVar = scope.variable("\(value)Bytes") let idVar = scope.variable("\(value)Id") - + printer.write("let \(idVar), \(bytesVar);") printer.write("if (\(isSomeVar)) {") printer.indent { @@ -274,13 +274,13 @@ struct IntrinsicJSFragment: Sendable { cleanupCode.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") } cleanupCode.write("}") - + return ["+\(isSomeVar)", "\(isSomeVar) ? \(idVar) : 0", "\(isSomeVar) ? \(bytesVar).length : 0"] case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName let caseIdVar = scope.variable("\(value)CaseId") let cleanupVar = scope.variable("\(value)Cleanup") - + printer.write("let \(caseIdVar), \(cleanupVar);") printer.write("if (\(isSomeVar)) {") printer.indent { @@ -291,7 +291,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("}") cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - + return ["+\(isSomeVar)", "\(isSomeVar) ? \(caseIdVar) : 0"] default: switch wrappedType { @@ -304,7 +304,7 @@ struct IntrinsicJSFragment: Sendable { } ) } - + static func optionalLiftReturn(wrappedType: BridgeType) -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: [], @@ -328,9 +328,13 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") case .swiftHeapObject(let className): let pointerVar = scope.variable("pointer") - printer.write("const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);") + printer.write( + "const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);" + ) printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;") - printer.write("const \(resultVar) = \(pointerVar) === null ? null : \(className).__construct(\(pointerVar));") + printer.write( + "const \(resultVar) = \(pointerVar) === null ? null : \(className).__construct(\(pointerVar));" + ) case .caseEnum: printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") @@ -340,13 +344,19 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") case .bool: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") + printer.write( + "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);" + ) printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") case .float: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") + printer.write( + "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);" + ) printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") case .double: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") + printer.write( + "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);" + ) printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") default: printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") @@ -363,7 +373,9 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(resultVar) = enumHelpers.\(base).raise(\(JSGlueVariableScope.reservedTmpRetTag), \(JSGlueVariableScope.reservedTmpRetStrings), \(JSGlueVariableScope.reservedTmpRetInts), \(JSGlueVariableScope.reservedTmpRetF32s), \(JSGlueVariableScope.reservedTmpRetF64s));") + printer.write( + "\(resultVar) = enumHelpers.\(base).raise(\(JSGlueVariableScope.reservedTmpRetTag), \(JSGlueVariableScope.reservedTmpRetStrings), \(JSGlueVariableScope.reservedTmpRetInts), \(JSGlueVariableScope.reservedTmpRetF32s), \(JSGlueVariableScope.reservedTmpRetF64s));" + ) } printer.write("}") default: @@ -401,7 +413,7 @@ struct IntrinsicJSFragment: Sendable { throw BridgeJSLinkError(message: "Namespace enums are not supported to be passed as parameters: \(string)") } } - + /// Returns a fragment that lifts a Wasm core value to a JS value for return values static func liftReturn(type: BridgeType) throws -> IntrinsicJSFragment { switch type { @@ -448,32 +460,8 @@ struct IntrinsicJSFragment: Sendable { message: "Void can't appear in parameters of imported JS functions" ) case .optional(let wrappedType): - let wrappedFragment = try liftParameter(type: wrappedType) - return IntrinsicJSFragment( - parameters: ["isSome"], - printCode: { arguments, scope, printer, cleanupCode in - let isSomeParam = arguments[0] - let resultVar = scope.variable("optResult") - - printer.write("let \(resultVar);") - printer.write("if (\(isSomeParam) === 0) {") - printer.indent { - printer.write("\(resultVar) = null;") - } - printer.write("} else {") - printer.indent { - let liftedResults = wrappedFragment.printCode([], scope, printer, cleanupCode) - - if let firstResult = liftedResults.first { - printer.write("\(resultVar) = \(firstResult);") - } else { - printer.write("\(resultVar) = undefined;") - } - } - printer.write("}") - - return [resultVar] - } + throw BridgeJSLinkError( + message: "Optional types are not supported for imported JS functions: \(wrappedType)" ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): @@ -508,37 +496,8 @@ struct IntrinsicJSFragment: Sendable { ) case .void: return .void case .optional(let wrappedType): - let wrappedFragment = try lowerReturn(type: wrappedType) - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, scope, printer, cleanupCode in - let value = arguments[0] - let isNullVar = scope.variable("isNull") - let isSomeVar = scope.variable("isSome") - let resultVars = scope.variable("results") - - printer.write("const \(isNullVar) = (\(value) === null || \(value) === undefined);") - printer.write("const \(isSomeVar) = \(isNullVar) ? 0 : 1;") - printer.write("let \(resultVars) = [\(isSomeVar)];") - - printer.write("if (!\(isNullVar)) {") - printer.indent { - let wrappedResults = wrappedFragment.printCode([value], scope, printer, cleanupCode) - for result in wrappedResults { - printer.write("\(resultVars).push(\(result));") - } - } - printer.write("} else {") - printer.indent { - let dummyResults = wrappedFragment.printCode(["null"], scope, printer, cleanupCode) - for result in dummyResults { - printer.write("\(resultVars).push(\(result));") - } - } - printer.write("}") - - return ["\(resultVars).slice()"] - } + throw BridgeJSLinkError( + message: "Optional types are not supported for imported JS functions: \(wrappedType)" ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): @@ -830,6 +789,66 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value"], printCode: { arguments, scope, printer, cleanup in printer.write("\(JSGlueVariableScope.reservedTmpParamF64s).push(\(arguments[0]));") + return [] + } + ) + case .optional(let wrappedType): + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanup in + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(value) != null;") + + switch wrappedType { + case .string: + let idVar = scope.variable("id") + printer.write("let \(idVar);") + printer.write("if (\(isSomeVar)) {") + printer.indent { + let bytesVar = scope.variable("bytes") + printer.write( + "let \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));" + ) + printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(bytesVar).length);") + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(idVar));") + } + printer.write("} else {") + printer.indent { + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(0);") + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(0);") + } + printer.write("}") + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + cleanup.write("if(\(idVar)) {") + cleanup.indent { + cleanup.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") + } + cleanup.write("}") + default: + switch wrappedType { + case .int: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) | 0) : 0);" + ) + case .bool: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) ? 1 : 0) : 0);" + ) + case .float: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamF32s).push(\(isSomeVar) ? Math.fround(\(value)) : 0.0);" + ) + case .double: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamF64s).push(\(isSomeVar) ? \(value) : 0.0);" + ) + default: () + } + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + } + return [] } ) @@ -890,6 +909,34 @@ struct IntrinsicJSFragment: Sendable { return [dVar] } ) + case .optional(let wrappedType): + return IntrinsicJSFragment( + parameters: [], + printCode: { arguments, scope, printer, cleanup in + let optVar = scope.variable("optional") + let isSomeVar = scope.variable("isSome") + + printer.write("const \(isSomeVar) = \(JSGlueVariableScope.reservedTmpRetInts).pop();") + printer.write("let \(optVar);") + printer.write("if (\(isSomeVar)) {") + printer.indent { + let wrappedFragment = associatedValuePopPayload(type: wrappedType) + let wrappedResults = wrappedFragment.printCode([], scope, printer, cleanup) + if let wrappedResult = wrappedResults.first { + printer.write("\(optVar) = \(wrappedResult);") + } else { + printer.write("\(optVar) = undefined;") + } + } + printer.write("} else {") + printer.indent { + printer.write("\(optVar) = null;") + } + printer.write("}") + + return [optVar] + } + ) default: return IntrinsicJSFragment( parameters: [], diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 0eccf2dc..32df40d1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -349,7 +349,7 @@ extension BridgeType { return nil } } - + /// Returns true if this type is optional public var isOptional: Bool { if case .optional = self { return true } diff --git a/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts b/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts index 269fc711..b53e2420 100644 --- a/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts +++ b/Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts @@ -6,7 +6,6 @@ export type BridgeType = | { "bool": {} } | { "jsObject": { "_0": string } | {} } | { "void": {} } - | { "optional": { "_0": BridgeType } } export type Parameter = { name: string; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift index 55040c70..e099ba85 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift @@ -58,3 +58,13 @@ enum Utilities { @JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult? { return result } + +@JS +enum APIOptionalResult { + case success(String?) + case failure(Int?, Bool?) + case status(Bool?, Int?, String?) +} +@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult? { + return result +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts deleted file mode 100644 index 358a9308..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalUnionTypes.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function roundTripOptionalNumber(value: number | null): number | null; -export function roundTripOptionalString(value: string | null): string | null; -export function roundTripOptionalBool(value: boolean | null): boolean | null; -export function roundTripOptionalClass(value: TestClass | null): TestClass | null; -export function testMixedOptionals(required: string, optional: number | null): boolean | null; - -export class TestClass { - optionalProperty: string | null; - - constructor(param: number | null); - - methodWithOptional(value: boolean | null): void; - methodReturningOptional(): string | null; -} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Optionals.swift similarity index 80% rename from Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift rename to Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Optionals.swift index 31bb648b..d256a875 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/OptionalParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Optionals.swift @@ -1,13 +1,14 @@ @JS class Greeter { @JS var name: String? - @JS init(name?: String) { + @JS init(name: String?) { self.name = name } + @JS func greet() -> String { return "Hello, " + (self.name ?? "stranger") + "!" } - + @JS func changeName(name: String?) { self.name = name } @@ -23,7 +24,7 @@ class OptionalPropertyHolder { @JS var optionalName: String? = nil @JS var optionalAge: Int? = nil @JS var optionalGreeter: Greeter? = nil - + @JS init() {} } @@ -70,7 +71,7 @@ func roundTripDouble(precision: Double?) -> Double? { return name } -@JS func roundTripWithSpaces(value: Optional < Double >) -> Optional < Double > { +@JS func roundTripWithSpaces(value: Optional) -> Optional { return value } @@ -80,10 +81,12 @@ typealias OptionalAge = Int? return age } +typealias OptionalNameAlias = Optional +@JS func roundTripOptionalAlias(name: OptionalNameAlias) -> OptionalNameAlias { + return name +} + @JS -func testMixedOptionals(firstName: String?, lastName: String?age: Int?, active: Bool) -> String? { - if let firstName = firstName, let lastName = lastName, let age = age { - return "\(firstName)-\(lastName)-\(age)-\(active)" - } +func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active: Bool) -> String? { return nil } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts index 584336e2..4195e9a2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts @@ -32,6 +32,17 @@ export const ComplexResult: { 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 const APIOptionalResult: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + readonly Status: 2; + }; +}; + +export type APIOptionalResult = + { tag: typeof APIOptionalResult.Tag.Success; param0: string | null } | { tag: typeof APIOptionalResult.Tag.Failure; param0: number | null; param1: boolean | null } | { tag: typeof APIOptionalResult.Tag.Status; param0: boolean | null; param1: number | null; param2: string | null } + export {}; declare global { @@ -69,6 +80,7 @@ export type Exports = { roundTripOptionalComplexResult(result: ComplexResult | null): ComplexResult | null; roundTripOptionalUtilitiesResult(result: Utilities.Result | null): Utilities.Result | null; roundTripOptionalNetworkingResult(result: NetworkingResult | null): NetworkingResult | null; + roundTripOptionalAPIOptionalResult(result: APIOptionalResult | null): APIOptionalResult | null; } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index d0557cf2..f7ca2dd8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -342,6 +342,143 @@ const __bjs_createNetworkingResultHelpers = () => { } }); }; +export const APIOptionalResult = { + Tag: { + Success: 0, + Failure: 1, + Status: 2, + } +}; + +const __bjs_createAPIOptionalResultHelpers = () => { + return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIOptionalResult.Tag.Success: { + const isSome = value.param0 != null; + let id; + if (isSome) { + let bytes = textEncoder.encode(value.param0); + id = swift.memory.retain(bytes); + tmpParamInts.push(bytes.length); + tmpParamInts.push(id); + } else { + tmpParamInts.push(0); + tmpParamInts.push(0); + } + tmpParamInts.push(isSome ? 1 : 0); + const cleanup = () => { + if(id) { + swift.memory.release(id); + } + }; + return { caseId: APIOptionalResult.Tag.Success, cleanup }; + } + case APIOptionalResult.Tag.Failure: { + const isSome = value.param1 != null; + tmpParamInts.push(isSome ? (value.param1 ? 1 : 0) : 0); + tmpParamInts.push(isSome ? 1 : 0); + const isSome1 = value.param0 != null; + tmpParamInts.push(isSome1 ? (value.param0 | 0) : 0); + tmpParamInts.push(isSome1 ? 1 : 0); + const cleanup = undefined; + return { caseId: APIOptionalResult.Tag.Failure, cleanup }; + } + case APIOptionalResult.Tag.Status: { + const isSome = value.param2 != null; + let id; + if (isSome) { + let bytes = textEncoder.encode(value.param2); + id = swift.memory.retain(bytes); + tmpParamInts.push(bytes.length); + tmpParamInts.push(id); + } else { + tmpParamInts.push(0); + tmpParamInts.push(0); + } + tmpParamInts.push(isSome ? 1 : 0); + const isSome1 = value.param1 != null; + tmpParamInts.push(isSome1 ? (value.param1 | 0) : 0); + tmpParamInts.push(isSome1 ? 1 : 0); + const isSome2 = value.param0 != null; + tmpParamInts.push(isSome2 ? (value.param0 ? 1 : 0) : 0); + tmpParamInts.push(isSome2 ? 1 : 0); + const cleanup = () => { + if(id) { + swift.memory.release(id); + } + }; + return { caseId: APIOptionalResult.Tag.Status, cleanup }; + } + default: throw new Error("Unknown APIOptionalResult tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { + const tag = tmpRetTag | 0; + switch (tag) { + case APIOptionalResult.Tag.Success: { + const isSome = tmpRetInts.pop(); + let optional; + if (isSome) { + const string = tmpRetStrings.pop(); + optional = string; + } else { + optional = null; + } + return { tag: APIOptionalResult.Tag.Success, param0: optional }; + } + case APIOptionalResult.Tag.Failure: { + const isSome = tmpRetInts.pop(); + let optional; + if (isSome) { + const bool = tmpRetInts.pop(); + optional = bool; + } else { + optional = null; + } + const isSome1 = tmpRetInts.pop(); + let optional1; + if (isSome1) { + const int = tmpRetInts.pop(); + optional1 = int; + } else { + optional1 = null; + } + return { tag: APIOptionalResult.Tag.Failure, param0: optional1, param1: optional }; + } + case APIOptionalResult.Tag.Status: { + const isSome = tmpRetInts.pop(); + let optional; + if (isSome) { + const string = tmpRetStrings.pop(); + optional = string; + } else { + optional = null; + } + const isSome1 = tmpRetInts.pop(); + let optional1; + if (isSome1) { + const int = tmpRetInts.pop(); + optional1 = int; + } else { + optional1 = null; + } + const isSome2 = tmpRetInts.pop(); + let optional2; + if (isSome2) { + const bool = tmpRetInts.pop(); + optional2 = bool; + } else { + optional2 = null; + } + return { tag: APIOptionalResult.Tag.Status, param0: optional2, param1: optional1, param2: optional }; + } + default: throw new Error("Unknown APIOptionalResult tag returned from Swift: " + String(tag)); + } + } + }); +}; if (typeof globalThis.Utilities === 'undefined') { globalThis.Utilities = {}; } @@ -504,6 +641,9 @@ export async function createInstantiator(options, swift) { const NetworkingResultHelpers = __bjs_createNetworkingResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.NetworkingResult = NetworkingResultHelpers; + const APIOptionalResultHelpers = __bjs_createAPIOptionalResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + enumHelpers.APIOptionalResult = APIOptionalResultHelpers; + setException = (error) => { instance.exports._swift_js_exception.value = swift.memory.retain(error) } @@ -622,6 +762,25 @@ export async function createInstantiator(options, swift) { if (resultCleanup) { resultCleanup(); } return optResult; }, + roundTripOptionalAPIOptionalResult: function bjs_roundTripOptionalAPIOptionalResult(result) { + const isSome = result != null; + let resultCaseId, resultCleanup; + if (isSome) { + const enumResult = enumHelpers.APIOptionalResult.lower(result); + resultCaseId = enumResult.caseId; + resultCleanup = enumResult.cleanup; + } + instance.exports.bjs_roundTripOptionalAPIOptionalResult(+isSome, isSome ? resultCaseId : 0); + const isNull = (tmpRetTag === -1); + let optResult; + if (isNull) { + optResult = null; + } else { + optResult = enumHelpers.APIOptionalResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + } + if (resultCleanup) { resultCleanup(); } + return optResult; + }, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts deleted file mode 100644 index 6fb85b8c..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, -// DO NOT EDIT. -// -// To update this file, just rebuild your project or run -// `swift package bridge-js`. - -export interface TestClass { - methodWithOptional(value: any): void; - methodReturningOptional(): any; - optionalProperty: any; -} -export type Exports = { -} -export type Imports = { - roundTripOptionalNumber(value: any): any; - roundTripOptionalString(value: any): any; - roundTripOptionalBool(value: any): any; - roundTripOptionalClass(value: any): any; - testMixedOptionals(required: string, optional: any): any; - TestClass: { - new(param: any): TestClass; - } -} -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/OptionalUnionTypes.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js deleted file mode 100644 index 37b338e0..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalUnionTypes.Import.js +++ /dev/null @@ -1,246 +0,0 @@ -// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, -// DO NOT EDIT. -// -// To update this file, just rebuild your project or run -// `swift package bridge-js`. - -export async function createInstantiator(options, swift) { - let instance; - let memory; - let setException; - const textDecoder = new TextDecoder("utf-8"); - const textEncoder = new TextEncoder("utf-8"); - let tmpRetString; - let tmpRetBytes; - let tmpRetException; - let tmpRetOptionalBool; - let tmpRetOptionalInt; - let tmpRetOptionalFloat; - let tmpRetOptionalDouble; - let tmpRetOptionalHeapObject; - let tmpRetTag; - let tmpRetStrings = []; - let tmpRetInts = []; - let tmpRetF32s = []; - let tmpRetF64s = []; - let tmpParamInts = []; - let tmpParamF32s = []; - let tmpParamF64s = []; - - 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); - tmpRetString = textDecoder.decode(bytes); - } - bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { - const source = swift.memory.getObject(sourceId); - const bytes = new Uint8Array(memory.buffer, bytesPtr); - bytes.set(source); - } - bjs["swift_js_make_js_string"] = function(ptr, len) { - const bytes = new Uint8Array(memory.buffer, ptr, len); - return swift.memory.retain(textDecoder.decode(bytes)); - } - bjs["swift_js_init_memory_with_result"] = function(ptr, len) { - const target = new Uint8Array(memory.buffer, ptr, len); - target.set(tmpRetBytes); - tmpRetBytes = undefined; - } - bjs["swift_js_throw"] = function(id) { - tmpRetException = swift.memory.retainByRef(id); - } - bjs["swift_js_retain"] = function(id) { - return swift.memory.retainByRef(id); - } - bjs["swift_js_release"] = function(id) { - swift.memory.release(id); - } - bjs["swift_js_push_tag"] = function(tag) { - tmpRetTag = tag; - } - bjs["swift_js_push_int"] = function(v) { - tmpRetInts.push(v | 0); - } - bjs["swift_js_push_f32"] = function(v) { - tmpRetF32s.push(Math.fround(v)); - } - bjs["swift_js_push_f64"] = function(v) { - tmpRetF64s.push(v); - } - bjs["swift_js_push_string"] = function(ptr, len) { - const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - tmpRetStrings.push(value); - } - bjs["swift_js_pop_param_int32"] = function() { - return tmpParamInts.pop(); - } - bjs["swift_js_pop_param_f32"] = function() { - return tmpParamF32s.pop(); - } - bjs["swift_js_pop_param_f64"] = function() { - return tmpParamF64s.pop(); - } - bjs["swift_js_return_optional_bool"] = function(isSome, value) { - if (isSome === 0) { - tmpRetOptionalBool = null; - } else { - tmpRetOptionalBool = value !== 0; - } - } - bjs["swift_js_return_optional_int"] = function(isSome, value) { - if (isSome === 0) { - tmpRetOptionalInt = null; - } else { - tmpRetOptionalInt = value | 0; - } - } - bjs["swift_js_return_optional_float"] = function(isSome, value) { - if (isSome === 0) { - tmpRetOptionalFloat = null; - } else { - tmpRetOptionalFloat = Math.fround(value); - } - } - bjs["swift_js_return_optional_double"] = function(isSome, value) { - if (isSome === 0) { - tmpRetOptionalDouble = null; - } else { - tmpRetOptionalDouble = value; - } - } - bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { - if (isSome === 0) { - tmpRetString = null; - } else { - const bytes = new Uint8Array(memory.buffer, ptr, len); - tmpRetString = textDecoder.decode(bytes); - } - } - bjs["swift_js_return_optional_object"] = function(isSome, objectId) { - if (isSome === 0) { - tmpRetString = null; - } else { - tmpRetString = swift.memory.getObject(objectId); - } - } - bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { - if (isSome === 0) { - tmpRetOptionalHeapObject = null; - } else { - tmpRetOptionalHeapObject = pointer; - } - } - const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; - TestModule["bjs_roundTripOptionalNumber"] = function bjs_roundTripOptionalNumber(value) { - try { - let ret = imports.roundTripOptionalNumber(swift.memory.getObject(value)); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_roundTripOptionalString"] = function bjs_roundTripOptionalString(value) { - try { - let ret = imports.roundTripOptionalString(swift.memory.getObject(value)); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_roundTripOptionalBool"] = function bjs_roundTripOptionalBool(value) { - try { - let ret = imports.roundTripOptionalBool(swift.memory.getObject(value)); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_roundTripOptionalClass"] = function bjs_roundTripOptionalClass(value) { - try { - let ret = imports.roundTripOptionalClass(swift.memory.getObject(value)); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_testMixedOptionals"] = function bjs_testMixedOptionals(required, optional) { - try { - const requiredObject = swift.memory.getObject(required); - swift.memory.release(required); - let ret = imports.testMixedOptionals(requiredObject, swift.memory.getObject(optional)); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_TestClass_init"] = function bjs_TestClass_init(param) { - try { - return swift.memory.retain(new imports.TestClass(swift.memory.getObject(param))); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_TestClass_optionalProperty_get"] = function bjs_TestClass_optionalProperty_get(self) { - try { - let ret = swift.memory.getObject(self).optionalProperty; - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_TestClass_optionalProperty_set"] = function bjs_TestClass_optionalProperty_set(self, newValue) { - try { - swift.memory.getObject(self).optionalProperty = swift.memory.getObject(newValue); - } catch (error) { - setException(error); - return 0 - } - } - TestModule["bjs_TestClass_methodWithOptional"] = function bjs_TestClass_methodWithOptional(self, value) { - try { - swift.memory.getObject(self).methodWithOptional(swift.memory.getObject(value)); - } catch (error) { - setException(error); - } - } - TestModule["bjs_TestClass_methodReturningOptional"] = function bjs_TestClass_methodReturningOptional(self) { - try { - let ret = swift.memory.getObject(self).methodReturningOptional(); - return swift.memory.retain(ret); - } catch (error) { - setException(error); - return 0 - } - } - }, - setInstance: (i) => { - instance = i; - memory = instance.exports.memory; - - setException = (error) => { - instance.exports._swift_js_exception.value = swift.memory.retain(error) - } - }, - /** @param {WebAssembly.Instance} instance */ - createExports: (instance) => { - const js = swift.memory.heap; - return { - }; - }, - } -} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.d.ts similarity index 92% rename from Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts rename to Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.d.ts index ab194d83..5f63e9db 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.d.ts @@ -23,7 +23,7 @@ export interface OptionalPropertyHolder extends SwiftHeapObject { } export type Exports = { Greeter: { - new(name: string): Greeter; + new(name: string | null): Greeter; } OptionalPropertyHolder: { new(): OptionalPropertyHolder; @@ -41,7 +41,8 @@ export type Exports = { roundTripMixedSwiftSyntax(name: string | null): string | null; roundTripWithSpaces(value: number | null): number | null; roundTripAlias(age: number | null): number | null; - testMixedOptionals(firstName: string | null, lastName: string | null): string | null; + roundTripOptionalAlias(name: string | null): string | null; + testMixedOptionals(firstName: string | null, lastName: string | null, age: number | null, active: boolean): string | null; } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.js similarity index 93% rename from Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js rename to Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.js index d397efaa..f67a06e8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/OptionalParameters.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.Export.js @@ -187,10 +187,16 @@ export async function createInstantiator(options, swift) { } constructor(name) { - const nameBytes = textEncoder.encode(name); - const nameId = swift.memory.retain(nameBytes); - const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length); - swift.memory.release(nameId); + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + } + const ret = instance.exports.bjs_Greeter_init(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + if (nameId != undefined) { + swift.memory.release(nameId); + } return Greeter.__construct(ret); } greet() { @@ -415,7 +421,22 @@ export async function createInstantiator(options, swift) { tmpRetOptionalInt = undefined; return optResult; }, - testMixedOptionals: function bjs_testMixedOptionals(firstName, lastName) { + roundTripOptionalAlias: function bjs_roundTripOptionalAlias(name) { + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + } + instance.exports.bjs_roundTripOptionalAlias(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId != undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + testMixedOptionals: function bjs_testMixedOptionals(firstName, lastName, age, active) { const isSome = firstName != null; let firstNameId, firstNameBytes; if (isSome) { @@ -428,7 +449,8 @@ export async function createInstantiator(options, swift) { lastNameBytes = textEncoder.encode(lastName); lastNameId = swift.memory.retain(lastNameBytes); } - instance.exports.bjs_testMixedOptionals(+isSome, isSome ? firstNameId : 0, isSome ? firstNameBytes.length : 0, +isSome1, isSome1 ? lastNameId : 0, isSome1 ? lastNameBytes.length : 0); + const isSome2 = age != null; + instance.exports.bjs_testMixedOptionals(+isSome, isSome ? firstNameId : 0, isSome ? firstNameBytes.length : 0, +isSome1, isSome1 ? lastNameId : 0, isSome1 ? lastNameBytes.length : 0, +isSome2, isSome2 ? age : 0, active); const optResult = tmpRetString; tmpRetString = undefined; if (firstNameId != undefined) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json index 72dc8b44..1f2b15fe 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -347,6 +347,94 @@ "API" ], "swiftCallName" : "NetworkingResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "name" : "status" + } + ], + "emitStyle" : "const", + "name" : "APIOptionalResult", + "swiftCallName" : "APIOptionalResult" } ], "functions" : [ @@ -605,6 +693,38 @@ } } } + }, + { + "abiName" : "bjs_roundTripOptionalAPIOptionalResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalAPIOptionalResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIOptionalResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIOptionalResult" + } + } + } + } } ], "moduleName" : "TestModule" diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift index fd414ece..008eeda0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift @@ -198,6 +198,68 @@ extension NetworkingResult: _BridgedSwiftAssociatedValueEnum { } } +extension APIOptionalResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIOptionalResult { + switch caseId { + case 0: + return .success(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + case 1: + return .failure(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + case 2: + return .status(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + default: + fatalError("Unknown APIOptionalResult case ID: \(caseId)") + } + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_push_tag(Int32(0)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + var __bjs_str_param0 = __bjs_unwrapped_param0 + __bjs_str_param0.withUTF8 { ptr in + _swift_js_push_string(ptr.baseAddress, Int32(ptr.count)) + } + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + case .failure(let param0, let param1): + _swift_js_push_tag(Int32(1)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + _swift_js_push_int(Int32(__bjs_unwrapped_param0)) + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + let __bjs_isSome_param1 = param1 != nil + if let __bjs_unwrapped_param1 = param1 { + _swift_js_push_int(__bjs_unwrapped_param1 ? 1 : 0) + } + _swift_js_push_int(__bjs_isSome_param1 ? 1 : 0) + case .status(let param0, let param1, let param2): + _swift_js_push_tag(Int32(2)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + _swift_js_push_int(__bjs_unwrapped_param0 ? 1 : 0) + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + let __bjs_isSome_param1 = param1 != nil + if let __bjs_unwrapped_param1 = param1 { + _swift_js_push_int(Int32(__bjs_unwrapped_param1)) + } + _swift_js_push_int(__bjs_isSome_param1 ? 1 : 0) + let __bjs_isSome_param2 = param2 != nil + if let __bjs_unwrapped_param2 = param2 { + var __bjs_str_param2 = __bjs_unwrapped_param2 + __bjs_str_param2.withUTF8 { ptr in + _swift_js_push_string(ptr.baseAddress, Int32(ptr.count)) + } + } + _swift_js_push_int(__bjs_isSome_param2 ? 1 : 0) + } + } +} + @_expose(wasm, "bjs_handle") @_cdecl("bjs_handle") public func _bjs_handle(result: Int32) -> Void { @@ -304,4 +366,15 @@ public func _bjs_roundTripOptionalNetworkingResult(resultIsSome: Int32, resultCa #else fatalError("Only available on WebAssembly") #endif +} + +@_expose(wasm, "bjs_roundTripOptionalAPIOptionalResult") +@_cdecl("bjs_roundTripOptionalAPIOptionalResult") +public func _bjs_roundTripOptionalAPIOptionalResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalAPIOptionalResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + 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/OptionalParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json similarity index 91% rename from Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json rename to Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json index e3fac8ed..28ac1150 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json @@ -12,8 +12,12 @@ "label" : "name", "name" : "name", "type" : { - "string" : { + "optional" : { + "_0" : { + "string" : { + } + } } } } @@ -562,6 +566,38 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalAlias", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalAlias", + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, { "abiName" : "bjs_testMixedOptionals", "effects" : { @@ -595,6 +631,28 @@ } } } + }, + { + "label" : "age", + "name" : "age", + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "label" : "active", + "name" : "active", + "type" : { + "bool" : { + + } + } } ], "returnType" : { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.swift similarity index 93% rename from Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift rename to Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.swift index 891fc7f0..9cd9e1f6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/OptionalParameters.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.swift @@ -149,11 +149,22 @@ public func _bjs_roundTripAlias(ageIsSome: Int32, ageValue: Int32) -> Void { #endif } +@_expose(wasm, "bjs_roundTripOptionalAlias") +@_cdecl("bjs_roundTripOptionalAlias") +public func _bjs_roundTripOptionalAlias(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalAlias(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_testMixedOptionals") @_cdecl("bjs_testMixedOptionals") -public func _bjs_testMixedOptionals(firstNameIsSome: Int32, firstNameBytes: Int32, firstNameLength: Int32, lastNameIsSome: Int32, lastNameBytes: Int32, lastNameLength: Int32) -> Void { +public func _bjs_testMixedOptionals(firstNameIsSome: Int32, firstNameBytes: Int32, firstNameLength: Int32, lastNameIsSome: Int32, lastNameBytes: Int32, lastNameLength: Int32, ageIsSome: Int32, ageValue: Int32, active: Int32) -> Void { #if arch(wasm32) - let ret = testMixedOptionals(firstName: Optional.bridgeJSLiftParameter(firstNameIsSome, firstNameBytes, firstNameLength), lastName: Optional.bridgeJSLiftParameter(lastNameIsSome, lastNameBytes, lastNameLength)) + let ret = testMixedOptionals(firstName: Optional.bridgeJSLiftParameter(firstNameIsSome, firstNameBytes, firstNameLength), lastName: Optional.bridgeJSLiftParameter(lastNameIsSome, lastNameBytes, lastNameLength), age: Optional.bridgeJSLiftParameter(ageIsSome, ageValue), active: Bool.bridgeJSLiftParameter(active)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -162,9 +173,9 @@ public func _bjs_testMixedOptionals(firstNameIsSome: Int32, firstNameBytes: Int3 @_expose(wasm, "bjs_Greeter_init") @_cdecl("bjs_Greeter_init") -public func _bjs_Greeter_init(nameBytes: Int32, nameLength: Int32) -> UnsafeMutableRawPointer { +public func _bjs_Greeter_init(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> UnsafeMutableRawPointer { #if arch(wasm32) - let ret = Greeter(name: String.bridgeJSLiftParameter(nameBytes, nameLength)) + let ret = Greeter(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift deleted file mode 100644 index e660592c..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/OptionalUnionTypes.swift +++ /dev/null @@ -1,176 +0,0 @@ -// 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 - -func roundTripOptionalNumber(_ value: JSObject) throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalNumber") - func bjs_roundTripOptionalNumber(_ value: Int32) -> Int32 - #else - func bjs_roundTripOptionalNumber(_ value: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_roundTripOptionalNumber(value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) -} - -func roundTripOptionalString(_ value: JSObject) throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalString") - func bjs_roundTripOptionalString(_ value: Int32) -> Int32 - #else - func bjs_roundTripOptionalString(_ value: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_roundTripOptionalString(value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) -} - -func roundTripOptionalBool(_ value: JSObject) throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalBool") - func bjs_roundTripOptionalBool(_ value: Int32) -> Int32 - #else - func bjs_roundTripOptionalBool(_ value: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_roundTripOptionalBool(value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) -} - -func roundTripOptionalClass(_ value: JSObject) throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_roundTripOptionalClass") - func bjs_roundTripOptionalClass(_ value: Int32) -> Int32 - #else - func bjs_roundTripOptionalClass(_ value: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_roundTripOptionalClass(value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) -} - -func testMixedOptionals(_ required: String, _ optional: JSObject) throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_testMixedOptionals") - func bjs_testMixedOptionals(_ required: Int32, _ optional: Int32) -> Int32 - #else - func bjs_testMixedOptionals(_ required: Int32, _ optional: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_testMixedOptionals(required.bridgeJSLowerParameter(), optional.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) -} - -struct TestClass: _JSBridgedClass { - let jsObject: JSObject - - init(unsafelyWrapping jsObject: JSObject) { - self.jsObject = jsObject - } - - init(_ param: JSObject) throws(JSException) { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_TestClass_init") - func bjs_TestClass_init(_ param: Int32) -> Int32 - #else - func bjs_TestClass_init(_ param: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_TestClass_init(param.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - self.jsObject = JSObject(id: UInt32(bitPattern: ret)) - } - - var optionalProperty: JSObject { - get throws(JSException) { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_TestClass_optionalProperty_get") - func bjs_TestClass_optionalProperty_get(_ self: Int32) -> Int32 - #else - func bjs_TestClass_optionalProperty_get(_ self: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_TestClass_optionalProperty_get(self.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) - } - } - - func setOptionalProperty(_ newValue: JSObject) throws(JSException) -> Void { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_TestClass_optionalProperty_set") - func bjs_TestClass_optionalProperty_set(_ self: Int32, _ newValue: Int32) -> Void - #else - func bjs_TestClass_optionalProperty_set(_ self: Int32, _ newValue: Int32) -> Void { - fatalError("Only available on WebAssembly") - } - #endif - bjs_TestClass_optionalProperty_set(self.bridgeJSLowerParameter(), newValue.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - } - - func methodWithOptional(_ value: JSObject) throws(JSException) -> Void { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_TestClass_methodWithOptional") - func bjs_TestClass_methodWithOptional(_ self: Int32, _ value: Int32) -> Void - #else - func bjs_TestClass_methodWithOptional(_ self: Int32, _ value: Int32) -> Void { - fatalError("Only available on WebAssembly") - } - #endif - bjs_TestClass_methodWithOptional(self.bridgeJSLowerParameter(), value.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - } - - func methodReturningOptional() throws(JSException) -> JSObject { - #if arch(wasm32) - @_extern(wasm, module: "Check", name: "bjs_TestClass_methodReturningOptional") - func bjs_TestClass_methodReturningOptional(_ self: Int32) -> Int32 - #else - func bjs_TestClass_methodReturningOptional(_ self: Int32) -> Int32 { - fatalError("Only available on WebAssembly") - } - #endif - let ret = bjs_TestClass_methodReturningOptional(self.bridgeJSLowerParameter()) - if let error = _swift_js_take_exception() { - throw error - } - return JSObject.bridgeJSLiftReturn(ret) - } - -} \ No newline at end of file diff --git a/Sources/JavaScriptKit/BridgeJSInstrincics.swift b/Sources/JavaScriptKit/BridgeJSInstrincics.swift index 5a664c48..d8ddf8fd 100644 --- a/Sources/JavaScriptKit/BridgeJSInstrincics.swift +++ b/Sources/JavaScriptKit/BridgeJSInstrincics.swift @@ -309,11 +309,7 @@ public protocol _BridgedSwiftEnumNoPayload {} /// A protocol that Swift case enum types (enums without raw values or associated values) conform to. /// /// The conformance is automatically synthesized by the BridgeJS code generator. -/// Case enums are lowered to Int32 values representing their case index. public protocol _BridgedSwiftCaseEnum { - // MARK: ImportTS & ExportSwift - @_spi(BridgeJS) consuming func bridgeJSLowerParameter() -> Int32 - @_spi(BridgeJS) static func bridgeJSLiftReturn(_ value: Int32) -> Self @_spi(BridgeJS) static func bridgeJSLiftParameter(_ value: Int32) -> Self @_spi(BridgeJS) consuming func bridgeJSLowerReturn() -> Int32 } @@ -321,9 +317,7 @@ public protocol _BridgedSwiftCaseEnum { /// A protocol that Swift associated value enum types conform to. /// /// The conformance is automatically synthesized by the BridgeJS code generator. -/// Associated value enums use a stack-based ABI for complex data transfer. public protocol _BridgedSwiftAssociatedValueEnum { - // MARK: ImportTS & ExportSwift @_spi(BridgeJS) static func bridgeJSLiftParameter(_ caseId: Int32) -> Self @_spi(BridgeJS) consuming func bridgeJSLowerReturn() -> Void } @@ -448,26 +442,17 @@ where Self: RawRepresentable, RawValue: _BridgedSwiftTypeLoweredIntoSingleWasmCo extension Optional where Wrapped == Bool { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { - switch self { - case .none: - return (0, 0) - case .some(let value): - return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value - } - } - + + @available(*, unavailable, message: "Optional Bool type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional Bool type is not supported to be passed to imported JS functions") @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Bool? { - if isSome == 0 { - return nil - } else { - return Bool.bridgeJSLiftReturn(wrappedValue) - } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Bool? { if isSome == 0 { return nil @@ -475,7 +460,7 @@ extension Optional where Wrapped == Bool { return Bool.bridgeJSLiftParameter(wrappedValue) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_bool") @@ -486,7 +471,7 @@ extension Optional where Wrapped == Bool { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_return_optional_bool(0, 0) @@ -499,26 +484,15 @@ extension Optional where Wrapped == Bool { /// Optional support for Int extension Optional where Wrapped == Int { // MARK: ImportTS - - @_spi(BridgeJS) public func bridgeJSLowerParameter() -> (Int32, Int32) { - switch self { - case .none: - return (0, 0) // isSome=0, dummy value=0 - case .some(let value): - return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value - } - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Int? { - if isSome == 0 { - return nil - } else { - return Int.bridgeJSLiftReturn(wrappedValue) - } - } - + + @available(*, unavailable, message: "Optional Int type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional Int type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Int? { return nil } + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Int? { if isSome == 0 { return nil @@ -526,7 +500,7 @@ extension Optional where Wrapped == Int { return Int.bridgeJSLiftParameter(wrappedValue) } } - + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_int") @@ -537,7 +511,7 @@ extension Optional where Wrapped == Int { _onlyAvailableOnWasm() } #endif - + switch self { case .none: _swift_js_return_optional_int(0, 0) @@ -548,34 +522,24 @@ extension Optional where Wrapped == Int { } extension Optional where Wrapped == String { // MARK: ImportTS - - @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Int32 { - switch self { - case .none: - return 0 - case .some(let value): - return value.bridgeJSLowerParameter() - } - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> String? { - if isSome == 0 { - return nil - } else { - return String.bridgeJSLiftReturn(isSome) - } - } - + + @available(*, unavailable, message: "Optional String type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional String type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> String? { return nil } + // MARK: ExportSwift - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ bytes: Int32, _ count: Int32) -> String? { + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ bytes: Int32, _ count: Int32) -> String? + { if isSome == 0 { return nil } else { return String.bridgeJSLiftParameter(bytes, count) } } - + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_string") @@ -586,7 +550,7 @@ extension Optional where Wrapped == String { _onlyAvailableOnWasm() } #endif - + switch self { case .none: _swift_js_return_optional_string(0, nil, 0) @@ -599,26 +563,17 @@ extension Optional where Wrapped == String { } extension Optional where Wrapped == JSObject { // MARK: ImportTS - - @_spi(BridgeJS) public func bridgeJSLowerParameter() -> (Int32, Int32) { - switch self { - case .none: - return (0, 0) - case .some(let value): - return (1, value.bridgeJSLowerParameter()) - } - } - + + @available(*, unavailable, message: "Optional JSObject type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional JSObject type is not supported to be passed to imported JS functions") @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ objectId: Int32) -> JSObject? { - if isSome == 0 { - return nil - } else { - return JSObject.bridgeJSLiftReturn(objectId) - } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> JSObject? { if isSome == 0 { return nil @@ -626,7 +581,7 @@ extension Optional where Wrapped == JSObject { return JSObject.bridgeJSLiftParameter(objectId) } } - + @_spi(BridgeJS) public func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_object") @@ -637,7 +592,7 @@ extension Optional where Wrapped == JSObject { _onlyAvailableOnWasm() } #endif - + switch self { case .none: _swift_js_return_optional_object(0, 0) @@ -651,22 +606,34 @@ extension Optional where Wrapped == JSObject { /// Optional support for Swift heap objects extension Optional where Wrapped: _BridgedSwiftHeapObject { // MARK: ImportTS - @available(*, unavailable, message: "Optional Swift heap objects are not supported to be passed to imported JS functions") + @available( + *, + unavailable, + message: "Optional Swift heap objects are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Void {} - - @available(*, unavailable, message: "Optional Swift heap objects are not supported to be returned from imported JS functions") - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer) -> Void {} - + + @available( + *, + unavailable, + message: "Optional Swift heap objects are not supported to be returned from imported JS functions" + ) + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer) -> Void { + } + // MARK: ExportSwift - - @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ isSome: Int32, _ pointer: UnsafeMutableRawPointer) -> Optional { + + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ pointer: UnsafeMutableRawPointer + ) -> Optional { if isSome == 0 { return nil } else { return Wrapped.bridgeJSLiftParameter(pointer) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_heap_object") @@ -677,7 +644,7 @@ extension Optional where Wrapped: _BridgedSwiftHeapObject { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_return_optional_heap_object(0, nil) @@ -689,26 +656,17 @@ extension Optional where Wrapped: _BridgedSwiftHeapObject { } extension Optional where Wrapped == Float { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float32) { - switch self { - case .none: - return (0, 0.0) - case .some(let value): - return (1, value.bridgeJSLowerParameter()) - } - } - + + @available(*, unavailable, message: "Optional Float type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional Float type is not supported to be passed to imported JS functions") @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float32) -> Float? { - if isSome == 0 { - return nil - } else { - return Float.bridgeJSLiftReturn(wrappedValue) - } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Float? { if isSome == 0 { return nil @@ -716,7 +674,7 @@ extension Optional where Wrapped == Float { return Float.bridgeJSLiftParameter(wrappedValue) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_float") @@ -727,7 +685,7 @@ extension Optional where Wrapped == Float { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_return_optional_float(0, 0.0) @@ -740,26 +698,17 @@ extension Optional where Wrapped == Float { /// Optional support for Double extension Optional where Wrapped == Double { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float64) { - switch self { - case .none: - return (0, 0.0) // isSome=0, dummy value=0.0 - case .some(let value): - return (1, value.bridgeJSLowerParameter()) // isSome=1, actual value - } - } - + + @available(*, unavailable, message: "Optional Double type is not supported to be passed to imported JS functions") + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available(*, unavailable, message: "Optional Double type is not supported to be passed to imported JS functions") @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float64) -> Double? { - if isSome == 0 { - return nil - } else { - return Double.bridgeJSLiftReturn(wrappedValue) - } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Double? { if isSome == 0 { return nil @@ -767,7 +716,7 @@ extension Optional where Wrapped == Double { return Double.bridgeJSLiftParameter(wrappedValue) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_double") @@ -778,7 +727,7 @@ extension Optional where Wrapped == Double { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_return_optional_double(0, 0.0) @@ -791,26 +740,25 @@ extension Optional where Wrapped == Double { /// Optional support for case enums extension Optional where Wrapped: _BridgedSwiftCaseEnum { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { - switch self { - case .none: - return (0, 0) // isSome=0, dummy value=0 - case .some(let value): - return (1, value.bridgeJSLowerParameter()) // isSome=1, actual enum value - } - } - + + @available( + *, + unavailable, + message: "Optional case enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional case enum types are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { - if isSome == 0 { - return nil - } else { - return Wrapped.bridgeJSLiftReturn(wrappedValue) - } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { if isSome == 0 { return nil @@ -818,7 +766,7 @@ extension Optional where Wrapped: _BridgedSwiftCaseEnum { return Wrapped.bridgeJSLiftParameter(wrappedValue) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_return_optional_int") @@ -829,7 +777,7 @@ extension Optional where Wrapped: _BridgedSwiftCaseEnum { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_return_optional_int(0, 0) @@ -845,7 +793,7 @@ public protocol _BridgedSwiftTypeLoweredIntoVoidType { } extension Optional where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { switch consume self { case .none: () @@ -857,135 +805,162 @@ extension Optional where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { // MARK: Optional Raw Value Enum Support -/// Optional support for String raw value enums (like Theme: String) extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == String { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Int32 { - let optionalRawValue: String? = self?.rawValue - return optionalRawValue.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> Wrapped? { - let optionalRawValue = String?.bridgeJSLiftReturn(isSome) - return optionalRawValue.flatMap { Wrapped(rawValue: $0) } - } - + + @available( + *, + unavailable, + message: "Optional String raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional String raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32) -> Wrapped? { return nil } + // MARK: ExportSwift - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ bytes: Int32, _ count: Int32) -> Wrapped? { + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ bytes: Int32, + _ count: Int32 + ) -> Wrapped? { let optionalRawValue = String?.bridgeJSLiftParameter(isSome, bytes, count) return optionalRawValue.flatMap { Wrapped(rawValue: $0) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let optionalRawValue: String? = self?.rawValue optionalRawValue.bridgeJSLowerReturn() } } -/// Optional support for Int raw value enums extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Int { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { - let optionalRawValue: Int? = self?.rawValue - return optionalRawValue.bridgeJSLowerParameter() - } - + + @available( + *, + unavailable, + message: "Optional Int raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional Int raw value enum types are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { - let optionalRawValue = Int?.bridgeJSLiftReturn(isSome, wrappedValue) - return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { let optionalRawValue = Int?.bridgeJSLiftParameter(isSome, wrappedValue) return optionalRawValue.flatMap { Wrapped(rawValue: $0) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let optionalRawValue: Int? = self?.rawValue optionalRawValue.bridgeJSLowerReturn() } } -/// Optional support for Bool raw value enums extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Bool { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Int32) { - let optionalRawValue: Bool? = self?.rawValue - return optionalRawValue.bridgeJSLowerParameter() - } - + + @available( + *, + unavailable, + message: "Optional Bool raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional Bool raw value enum types are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { - let optionalRawValue = Bool?.bridgeJSLiftReturn(isSome, wrappedValue) - return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Wrapped? { let optionalRawValue = Bool?.bridgeJSLiftParameter(isSome, wrappedValue) return optionalRawValue.flatMap { Wrapped(rawValue: $0) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let optionalRawValue: Bool? = self?.rawValue optionalRawValue.bridgeJSLowerReturn() } } -/// Optional support for Float raw value enums extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Float { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float32) { - let optionalRawValue: Float? = self?.rawValue - return optionalRawValue.bridgeJSLowerParameter() - } - + + @available( + *, + unavailable, + message: "Optional Float raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional Float raw value enum types are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float32) -> Wrapped? { - let optionalRawValue = Float?.bridgeJSLiftReturn(isSome, wrappedValue) - return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Wrapped? { let optionalRawValue = Float?.bridgeJSLiftParameter(isSome, wrappedValue) return optionalRawValue.flatMap { Wrapped(rawValue: $0) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let optionalRawValue: Float? = self?.rawValue optionalRawValue.bridgeJSLowerReturn() } } -/// Optional support for Double raw value enums extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Double { // MARK: ImportTS - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (Int32, Float64) { - let optionalRawValue: Double? = self?.rawValue - return optionalRawValue.bridgeJSLowerParameter() - } - + + @available( + *, + unavailable, + message: "Optional Double raw value enum types are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} + + @available( + *, + unavailable, + message: "Optional Double raw value enum types are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ wrappedValue: Float64) -> Wrapped? { - let optionalRawValue = Double?.bridgeJSLiftReturn(isSome, wrappedValue) - return optionalRawValue.flatMap { Wrapped(rawValue: $0) } + return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Wrapped? { let optionalRawValue = Double?.bridgeJSLiftParameter(isSome, wrappedValue) return optionalRawValue.flatMap { Wrapped(rawValue: $0) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let optionalRawValue: Double? = self?.rawValue optionalRawValue.bridgeJSLowerReturn() @@ -994,17 +969,26 @@ extension Optional where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepres // MARK: Optional Associated Value Enum Support -/// Optional support for associated value enums extension Optional where Wrapped: _BridgedSwiftAssociatedValueEnum { // MARK: ImportTS - @available(*, unavailable, message: "Optional associated value enums are not supported to be passed to imported JS functions") - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} - - @available(*, unavailable, message: "Optional associated value enums are not supported to be returned from imported JS functions") + @available( + *, + unavailable, + message: "Optional associated value enums are not supported to be passed to imported JS functions" + ) + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void { + fatalError("Optional associated value enum bridgeJSLowerParameter is not supported for ImportTS") + } + + @available( + *, + unavailable, + message: "Optional associated value enums are not supported to be passed to imported JS functions" + ) @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ isSome: Int32, _ caseId: Int32) -> Wrapped? { return nil } - + // MARK: ExportSwift - + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Wrapped? { if isSome == 0 { return nil @@ -1012,23 +996,22 @@ extension Optional where Wrapped: _BridgedSwiftAssociatedValueEnum { return Wrapped.bridgeJSLiftParameter(caseId) } } - + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { #if arch(wasm32) @_extern(wasm, module: "bjs", name: "swift_js_push_tag") func _swift_js_push_tag(_ tag: Int32) #else - /// Pushes an enum tag to the return tag storage func _swift_js_push_tag(_ tag: Int32) { _onlyAvailableOnWasm() } #endif - + switch consume self { case .none: _swift_js_push_tag(-1) // Use -1 as sentinel for null case .some(let value): - value.bridgeJSLowerReturn() // This will push to tmpRetTag and stacks + value.bridgeJSLowerReturn() } } } diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 571e6ffe..7e2b586e 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -469,7 +469,7 @@ enum ComplexResult { return name } -@JS func roundTripOptionalWithSpaces(value: Optional< Double >) -> Optional< Double > { +@JS func roundTripOptionalWithSpaces(value: Optional) -> Optional { return value } @@ -517,12 +517,22 @@ typealias OptionalAge = Int? @JS var optionalName: String? @JS var optionalAge: Int? = nil @JS var optionalGreeter: Greeter? = nil - + @JS init(optionalName: String?) { self.optionalName = optionalName } } +@JS +enum APIOptionalResult { + case success(String?) + case failure(Int?, Bool?) + case status(Bool?, Int?, String?) +} +@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult? { + 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 e18c0cbd..b629cda4 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -426,6 +426,68 @@ extension API.NetworkingResult: _BridgedSwiftAssociatedValueEnum { } } +extension APIOptionalResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIOptionalResult { + switch caseId { + case 0: + return .success(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + case 1: + return .failure(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + case 2: + return .status(Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()), Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + default: + fatalError("Unknown APIOptionalResult case ID: \(caseId)") + } + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_push_tag(Int32(0)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + var __bjs_str_param0 = __bjs_unwrapped_param0 + __bjs_str_param0.withUTF8 { ptr in + _swift_js_push_string(ptr.baseAddress, Int32(ptr.count)) + } + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + case .failure(let param0, let param1): + _swift_js_push_tag(Int32(1)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + _swift_js_push_int(Int32(__bjs_unwrapped_param0)) + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + let __bjs_isSome_param1 = param1 != nil + if let __bjs_unwrapped_param1 = param1 { + _swift_js_push_int(__bjs_unwrapped_param1 ? 1 : 0) + } + _swift_js_push_int(__bjs_isSome_param1 ? 1 : 0) + case .status(let param0, let param1, let param2): + _swift_js_push_tag(Int32(2)) + let __bjs_isSome_param0 = param0 != nil + if let __bjs_unwrapped_param0 = param0 { + _swift_js_push_int(__bjs_unwrapped_param0 ? 1 : 0) + } + _swift_js_push_int(__bjs_isSome_param0 ? 1 : 0) + let __bjs_isSome_param1 = param1 != nil + if let __bjs_unwrapped_param1 = param1 { + _swift_js_push_int(Int32(__bjs_unwrapped_param1)) + } + _swift_js_push_int(__bjs_isSome_param1 ? 1 : 0) + let __bjs_isSome_param2 = param2 != nil + if let __bjs_unwrapped_param2 = param2 { + var __bjs_str_param2 = __bjs_unwrapped_param2 + __bjs_str_param2.withUTF8 { ptr in + _swift_js_push_string(ptr.baseAddress, Int32(ptr.count)) + } + } + _swift_js_push_int(__bjs_isSome_param2 ? 1 : 0) + } + } +} + @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { @@ -1519,6 +1581,17 @@ public func _bjs_roundTripOptionalClass(valueIsSome: Int32, valueValue: UnsafeMu #endif } +@_expose(wasm, "bjs_roundTripOptionalAPIOptionalResult") +@_cdecl("bjs_roundTripOptionalAPIOptionalResult") +public func _bjs_roundTripOptionalAPIOptionalResult(resultIsSome: Int32, resultCaseId: Int32) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalAPIOptionalResult(result: Optional.bridgeJSLiftParameter(resultIsSome, resultCaseId)) + 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 e1c5be24..def3e68d 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -1354,6 +1354,94 @@ "API" ], "swiftCallName" : "API.NetworkingResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + } + ], + "name" : "failure" + }, + { + "associatedValues" : [ + { + "type" : { + "optional" : { + "_0" : { + "bool" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "name" : "status" + } + ], + "emitStyle" : "const", + "name" : "APIOptionalResult", + "swiftCallName" : "APIOptionalResult" } ], "functions" : [ @@ -3690,6 +3778,38 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalAPIOptionalResult", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalAPIOptionalResult", + "parameters" : [ + { + "label" : "result", + "name" : "result", + "type" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIOptionalResult" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "associatedValueEnum" : { + "_0" : "APIOptionalResult" + } + } + } + } + }, { "abiName" : "bjs_createPropertyHolder", "effects" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index f491bcf2..039c721e 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -1,7 +1,7 @@ // @ts-check import { - Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult + Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult } from '../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ @@ -528,6 +528,29 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(optionalsHolder.optionalGreeter, null); testPropertyGreeter.release(); optionalsHolder.release(); + + const aor1 = { tag: APIOptionalResult.Tag.Success, param0: "hello world" }; + const aor2 = { tag: APIOptionalResult.Tag.Success, param0: null }; + const aor3 = { tag: APIOptionalResult.Tag.Failure, param0: 404, param1: true }; + const aor4 = { tag: APIOptionalResult.Tag.Failure, param0: 404, param1: null }; + const aor5 = { tag: APIOptionalResult.Tag.Failure, param0: null, param1: null }; + const aor6 = { tag: APIOptionalResult.Tag.Status, param0: true, param1: 200, param2: "OK" }; + const aor7 = { tag: APIOptionalResult.Tag.Status, param0: true, param1: null, param2: "Partial" }; + const aor8 = { tag: APIOptionalResult.Tag.Status, param0: null, param1: null, param2: "Zero" }; + const aor9 = { tag: APIOptionalResult.Tag.Status, param0: false, param1: 500, param2: null }; + const aor10 = { tag: APIOptionalResult.Tag.Status, param0: null, param1: 0, param2: "Zero" }; + + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor1), aor1); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor2), aor2); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor3), aor3); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor4), aor4); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor5), aor5); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor6), aor6); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor7), aor7); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor8), aor8); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor9), aor9); + assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor10), aor10); + assert.equal(exports.roundTripOptionalAPIOptionalResult(null), null); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ From e41c6c4da860f5ca788563ec98e8fb6643c9e0cb Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 10 Sep 2025 15:57:00 +0200 Subject: [PATCH 4/5] BridgeJS: ./Utilities/bridge-js-generate.sh update --- .../Sources/Generated/BridgeJS.ExportSwift.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift index 2d9f61b9..d02d0fae 100644 --- a/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift +++ b/Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift @@ -6,8 +6,8 @@ @_spi(BridgeJS) import JavaScriptKit -private extension APIResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { +extension APIResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -26,7 +26,7 @@ private extension APIResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) @@ -52,8 +52,8 @@ private extension APIResult { } } -private extension ComplexResult { - static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { +extension ComplexResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult { switch caseId { case 0: return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) @@ -74,7 +74,7 @@ private extension ComplexResult { } } - func bridgeJSLowerReturn() { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { switch self { case .success(let param0): _swift_js_push_tag(Int32(0)) From 29064b968df3eeb99f7ccf542c630d164486d752 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 10 Sep 2025 16:19:53 +0200 Subject: [PATCH 5/5] BridgeJS: Add missing template functions --- Plugins/PackageToJS/Templates/instantiate.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Plugins/PackageToJS/Templates/instantiate.js b/Plugins/PackageToJS/Templates/instantiate.js index 9b35efd1..236b7020 100644 --- a/Plugins/PackageToJS/Templates/instantiate.js +++ b/Plugins/PackageToJS/Templates/instantiate.js @@ -39,6 +39,21 @@ async function createInstantiator(options, swift) { swift_js_throw: unexpectedBjsCall, swift_js_retain: unexpectedBjsCall, swift_js_release: unexpectedBjsCall, + swift_js_push_tag: unexpectedBjsCall, + swift_js_push_int: unexpectedBjsCall, + swift_js_push_f32: unexpectedBjsCall, + swift_js_push_f64: unexpectedBjsCall, + swift_js_push_string: unexpectedBjsCall, + swift_js_pop_param_int32: unexpectedBjsCall, + swift_js_pop_param_f32: unexpectedBjsCall, + swift_js_pop_param_f64: unexpectedBjsCall, + swift_js_return_optional_bool: unexpectedBjsCall, + swift_js_return_optional_int: unexpectedBjsCall, + swift_js_return_optional_string: unexpectedBjsCall, + swift_js_return_optional_double: unexpectedBjsCall, + swift_js_return_optional_float: unexpectedBjsCall, + swift_js_return_optional_heap_object: unexpectedBjsCall, + swift_js_return_optional_object: unexpectedBjsCall, } }, /** @param {WebAssembly.Instance} instance */