From fc7e237ae303d0a8b373ae98024083f7efaf91be Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 00:45:00 +0900 Subject: [PATCH 1/6] BridgeJS: Support `T | null` and `T | undefined` --- .../Sources/BridgeJSCore/ExportSwift.swift | 39 +- .../Sources/BridgeJSCore/ImportTS.swift | 30 +- .../BridgeJSCore/SwiftToSkeleton.swift | 24 ++ .../Sources/BridgeJSLink/BridgeJSLink.swift | 2 + .../Sources/BridgeJSLink/JSGlueGen.swift | 228 ++++++++-- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 16 +- .../TS2Swift/JavaScript/src/processor.js | 24 ++ .../JavaScriptKit/BridgeJSIntrinsics.swift | 19 +- Sources/JavaScriptKit/JSUndefinedOr.swift | 408 ++++++++++++++++++ .../BridgeJSRuntimeTests/ExportAPITests.swift | 23 + .../Generated/BridgeJS.Macros.swift | 4 + .../Generated/BridgeJS.swift | 195 +++++++++ .../Generated/JavaScript/BridgeJS.json | 267 ++++++++++++ .../BridgeJSRuntimeTests/ImportAPITests.swift | 20 + Tests/BridgeJSRuntimeTests/bridge-js.d.ts | 2 + Tests/prelude.mjs | 26 ++ tsconfig.json | 6 + 17 files changed, 1250 insertions(+), 83 deletions(-) create mode 100644 Sources/JavaScriptKit/JSUndefinedOr.swift create mode 100644 tsconfig.json diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index dc8b5e04..39281ddf 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -820,6 +820,8 @@ struct StackCodegen { return "\(raw: type.swiftType).bridgeJSLiftParameter(_swift_js_pop_i32())" case .optional(let wrappedType): return liftOptionalExpression(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return liftUndefinedExpression(wrappedType: wrappedType) case .array(let elementType): return liftArrayExpression(elementType: elementType) case .closure: @@ -839,7 +841,7 @@ struct StackCodegen { return liftArrayExpressionInline(elementType: elementType) case .swiftProtocol(let protocolName): return "[Any\(raw: protocolName)].bridgeJSLiftParameter()" - case .optional, .array, .closure: + case .optional, .undefinedOr, .array, .closure: return liftArrayExpressionInline(elementType: elementType) case .void, .namespaceEnum: fatalError("Invalid array element type: \(elementType)") @@ -883,11 +885,34 @@ struct StackCodegen { } }() """ - case .void, .namespaceEnum, .closure, .optional, .unsafePointer, .swiftProtocol: + case .undefinedOr, .void, .namespaceEnum, .closure, .optional, .unsafePointer, .swiftProtocol: fatalError("Invalid optional wrapped type: \(wrappedType)") } } + private func liftUndefinedExpression(wrappedType: BridgeType) -> ExprSyntax { + switch wrappedType { + case .string, .int, .uint, .bool, .float, .double, .jsObject, + .swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum: + return "JSUndefinedOr<\(raw: wrappedType.swiftType)>.bridgeJSLiftParameter()" + case .array(let elementType): + let arrayLift = liftArrayExpression(elementType: elementType) + let swiftTypeName = elementType.swiftType + return """ + { + let __isDefined = _swift_js_pop_i32() + if __isDefined == 0 { + return JSUndefinedOr<\(raw: swiftTypeName)>.undefined + } else { + return JSUndefinedOr<\(raw: swiftTypeName)>(optional: \(arrayLift)) + } + }() + """ + case .void, .namespaceEnum, .closure, .optional, .undefinedOr, .unsafePointer, .swiftProtocol: + fatalError("Invalid undefinedOr wrapped type: \(wrappedType)") + } + } + /// Generates statements to lower/push a value onto the stack. /// - Parameters: /// - type: The BridgeType to lower @@ -917,6 +942,8 @@ struct StackCodegen { return ["\(raw: accessor).bridgeJSLowerReturn()"] case .optional(let wrappedType): return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix) + case .undefinedOr(let wrappedType): + return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix) case .void, .namespaceEnum: return [] case .array(let elementType): @@ -938,7 +965,7 @@ struct StackCodegen { return ["\(raw: accessor).map { $0.jsObject }.bridgeJSLowerReturn()"] case .swiftProtocol(let protocolName): return ["\(raw: accessor).map { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"] - case .optional, .array, .closure: + case .optional, .undefinedOr, .array, .closure: return lowerArrayStatementsInline( elementType: elementType, accessor: accessor, @@ -1597,6 +1624,7 @@ extension BridgeType { case .swiftProtocol(let name): return "Any\(name)" case .void: return "Void" case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>" + case .undefinedOr(let wrappedType): return "JSUndefinedOr<\(wrappedType.swiftType)>" case .array(let elementType): return "[\(elementType.swiftType)]" case .caseEnum(let name): return name case .rawValueEnum(let name, _): return name @@ -1644,6 +1672,10 @@ extension BridgeType { var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters) return LiftingIntrinsicInfo(parameters: optionalParams) + case .undefinedOr(let wrappedType): + var params: [(name: String, type: WasmCoreType)] = [("isDefined", .i32)] + params.append(contentsOf: try wrappedType.liftParameterInfo().parameters) + return LiftingIntrinsicInfo(parameters: params) case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): return rawType.liftingIntrinsicInfo @@ -1693,6 +1725,7 @@ extension BridgeType { case .swiftProtocol: return .jsObject case .void: return .void case .optional: return .optional + case .undefinedOr: return .optional case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): return rawType.loweringIntrinsicInfo diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 7c391eea..7ac72cff 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -925,15 +925,15 @@ extension BridgeType { case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as parameters") case .optional(let wrappedType): - switch context { - case .importTS: - throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports") - case .exportSwift: - let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) - var params = [("isSome", WasmCoreType.i32)] - params.append(contentsOf: wrappedInfo.loweredParameters) - return LoweringParameterInfo(loweredParameters: params) - } + let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) + var params = [("isSome", WasmCoreType.i32)] + params.append(contentsOf: wrappedInfo.loweredParameters) + return LoweringParameterInfo(loweredParameters: params) + case .undefinedOr(let wrappedType): + let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) + var params = [("isDefined", WasmCoreType.i32)] + params.append(contentsOf: wrappedInfo.loweredParameters) + return LoweringParameterInfo(loweredParameters: params) case .array: switch context { case .importTS: @@ -1019,13 +1019,11 @@ extension BridgeType { case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as return values") case .optional(let wrappedType): - switch context { - case .importTS: - throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports") - case .exportSwift: - let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) - return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) - } + let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) + return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) + case .undefinedOr(let wrappedType): + let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) + return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) case .array: switch context { case .importTS: diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index b780012f..67f1421a 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -133,6 +133,30 @@ public final class SwiftToSkeleton { return .optional(baseType) } } + // JSUndefinedOr + if let identifierType = type.as(IdentifierTypeSyntax.self), + identifierType.name.text == "JSUndefinedOr", + let genericArgs = identifierType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = TypeSyntax(genericArgs.first?.argument) + { + if let baseType = lookupType(for: argType, errors: &errors) { + return .undefinedOr(baseType) + } + } + // JavaScriptKit.JSUndefinedOr + if let memberType = type.as(MemberTypeSyntax.self), + let baseType = memberType.baseType.as(IdentifierTypeSyntax.self), + baseType.name.text == "JavaScriptKit", + memberType.name.text == "JSUndefinedOr", + let genericArgs = memberType.genericArgumentClause?.arguments, + genericArgs.count == 1, + let argType = TypeSyntax(genericArgs.first?.argument) + { + if let wrappedType = lookupType(for: argType, errors: &errors) { + return .undefinedOr(wrappedType) + } + } // Optional if let identifierType = type.as(IdentifierTypeSyntax.self), identifierType.name.text == "Optional", diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 60b55cd4..0b1be8a1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -3405,6 +3405,8 @@ extension BridgeType { return "number" case .optional(let wrappedType): return "\(wrappedType.tsType) | null" + case .undefinedOr(let wrappedType): + return "\(wrappedType.tsType) | undefined" case .caseEnum(let name): return "\(name)Tag" case .rawValueEnum(let name, _): diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index c78a92de..ad27dc6f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -296,7 +296,10 @@ struct IntrinsicJSFragment: Sendable { ) } - static func optionalLiftParameter(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + static func optionalLiftParameter( + wrappedType: BridgeType, + absenceLiteral: String = "null" + ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["isSome", "wrappedValue"], printCode: { arguments, scope, printer, cleanupCode in @@ -306,9 +309,9 @@ struct IntrinsicJSFragment: Sendable { switch wrappedType { case .int, .float, .double, .caseEnum: - resultExpr = "\(isSome) ? \(wrappedValue) : null" + resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" case .bool: - resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : null" + resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : \(absenceLiteral)" case .string: let objectLabel = scope.variable("obj") printer.write("let \(objectLabel);") @@ -320,12 +323,12 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(wrappedValue));") } printer.write("}") - resultExpr = "\(isSome) ? \(objectLabel) : null" + resultExpr = "\(isSome) ? \(objectLabel) : \(absenceLiteral)" case .swiftHeapObject(let name): - resultExpr = "\(isSome) ? \(name).__construct(\(wrappedValue)) : null" + resultExpr = "\(isSome) ? \(name).__construct(\(wrappedValue)) : \(absenceLiteral)" case .jsObject: resultExpr = - "\(isSome) ? \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue)) : null" + "\(isSome) ? \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue)) : \(absenceLiteral)" case .rawValueEnum(_, let rawType): switch rawType { case .string: @@ -339,11 +342,11 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(wrappedValue));") } printer.write("}") - resultExpr = "\(isSome) ? \(objectLabel) : null" + resultExpr = "\(isSome) ? \(objectLabel) : \(absenceLiteral)" case .bool: - resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : null" + resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : \(absenceLiteral)" default: - resultExpr = "\(isSome) ? \(wrappedValue) : null" + resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" } case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName @@ -356,7 +359,7 @@ struct IntrinsicJSFragment: Sendable { ) } printer.write("}") - resultExpr = "\(isSome) ? \(enumVar) : null" + resultExpr = "\(isSome) ? \(enumVar) : \(absenceLiteral)" case .swiftStruct(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName let structVar = scope.variable("structValue") @@ -369,7 +372,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(structVar) = null;") + printer.write("\(structVar) = \(absenceLiteral);") } printer.write("}") resultExpr = structVar @@ -387,12 +390,12 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(arrayVar) = null;") + printer.write("\(arrayVar) = \(absenceLiteral);") } printer.write("}") resultExpr = arrayVar default: - resultExpr = "\(isSome) ? \(wrappedValue) : null" + resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" } return [resultExpr] @@ -514,7 +517,8 @@ struct IntrinsicJSFragment: Sendable { static func optionalLiftReturn( wrappedType: BridgeType, - context: BridgeContext = .exportSwift + context: BridgeContext = .exportSwift, + absenceLiteral: String = "null" ) -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: [], @@ -550,7 +554,7 @@ struct IntrinsicJSFragment: Sendable { ? "\(className).__construct(\(pointerVar))" : "_exports['\(className)'].__construct(\(pointerVar))" printer.write( - "const \(resultVar) = \(pointerVar) === null ? null : \(constructExpr);" + "const \(resultVar) = \(pointerVar) === null ? \(absenceLiteral) : \(constructExpr);" ) case .caseEnum: printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") @@ -586,7 +590,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("let \(resultVar);") printer.write("if (\(isNullVar)) {") printer.indent { - printer.write("\(resultVar) = null;") + printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("} else {") printer.indent { @@ -608,7 +612,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(resultVar) = null;") + printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("}") case .array(let elementType): @@ -625,7 +629,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(resultVar) = null;") + printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("}") default: @@ -637,7 +641,10 @@ struct IntrinsicJSFragment: Sendable { ) } - static func optionalLowerReturn(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + static func optionalLowerReturn( + wrappedType: BridgeType, + presenceCheck: (@Sendable (String) -> String)? = nil + ) throws -> IntrinsicJSFragment { switch wrappedType { case .void, .optional, .namespaceEnum, .closure: throw BridgeJSLinkError(message: "Unsupported optional wrapped type for protocol export: \(wrappedType)") @@ -649,7 +656,8 @@ struct IntrinsicJSFragment: Sendable { printCode: { arguments, scope, printer, cleanupCode in let value = arguments[0] let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(value) != null;") + let presenceExpr = presenceCheck?(value) ?? "\(value) != null" + printer.write("const \(isSomeVar) = \(presenceExpr);") switch wrappedType { case .bool: @@ -905,7 +913,7 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .optional(let wrappedType): + case .optional(let wrappedType), .undefinedOr(let wrappedType): return try closureOptionalLiftParameter(wrappedType: wrappedType) default: throw BridgeJSLinkError(message: "Unsupported closure parameter type for lifting: \(type)") @@ -1106,7 +1114,7 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .optional(let wrappedType): + case .optional(let wrappedType), .undefinedOr(let wrappedType): return try closureOptionalLowerReturn(wrappedType: wrappedType) default: throw BridgeJSLinkError(message: "Unsupported closure return type for lowering: \(type)") @@ -1303,19 +1311,28 @@ struct IntrinsicJSFragment: Sendable { ) case .optional(let wrappedType): return try closureOptionalLiftReturn(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return try closureOptionalLiftReturn(wrappedType: wrappedType, absenceLiteral: "undefined") default: throw BridgeJSLinkError(message: "Unsupported closure return type for lifting: \(type)") } } /// Handles optional return lifting for Swift closure returns - private static func closureOptionalLiftReturn(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + private static func closureOptionalLiftReturn( + wrappedType: BridgeType, + absenceLiteral: String = "null" + ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["invokeCall"], printCode: { arguments, scope, printer, cleanupCode in let invokeCall = arguments[0] printer.write("\(invokeCall);") - let baseFragment = optionalLiftReturn(wrappedType: wrappedType, context: .importTS) + let baseFragment = optionalLiftReturn( + wrappedType: wrappedType, + context: .importTS, + absenceLiteral: absenceLiteral + ) let lifted = baseFragment.printCode([], scope, printer, cleanupCode) if !lifted.isEmpty { printer.write("return \(lifted[0]);") @@ -1361,6 +1378,8 @@ struct IntrinsicJSFragment: Sendable { default: printer.write("return;") } + case .undefinedOr: + printer.write("return;") default: printer.write("return 0;") } @@ -1384,6 +1403,8 @@ struct IntrinsicJSFragment: Sendable { case .void: return .void case .optional(let wrappedType): return try .optionalLowerParameter(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return try .optionalLowerParameter(wrappedType: wrappedType) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1428,6 +1449,8 @@ struct IntrinsicJSFragment: Sendable { case .swiftProtocol: return .jsObjectLiftReturn case .void: return .void case .optional(let wrappedType): return .optionalLiftReturn(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return .optionalLiftReturn(wrappedType: wrappedType, absenceLiteral: "undefined") case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1486,14 +1509,9 @@ struct IntrinsicJSFragment: Sendable { message: "Void can't appear in parameters of imported JS functions" ) case .optional(let wrappedType): - switch context { - case .importTS: - throw BridgeJSLinkError( - message: "Optional types are not supported for imported JS functions: \(wrappedType)" - ) - case .exportSwift: - return try .optionalLiftParameter(wrappedType: wrappedType) - } + return try .optionalLiftParameter(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return try .optionalLiftParameter(wrappedType: wrappedType, absenceLiteral: "undefined") case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1584,14 +1602,12 @@ struct IntrinsicJSFragment: Sendable { case .swiftProtocol: return .jsObjectLowerReturn case .void: return .void case .optional(let wrappedType): - switch context { - case .importTS: - throw BridgeJSLinkError( - message: "Optional types are not supported for imported JS functions: \(wrappedType)" - ) - case .exportSwift: - return try .optionalLowerReturn(wrappedType: wrappedType) - } + return try .optionalLowerReturn(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return try .optionalLowerReturn( + wrappedType: wrappedType, + presenceCheck: { value in "\(value) !== undefined" } + ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1998,6 +2014,67 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") } + return [] + } + ) + case .undefinedOr(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) !== undefined;") + + 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("}") + case .int, .uint: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) | 0) : 0);" + ) + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + case .bool: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) ? 1 : 0) : 0);" + ) + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + case .float: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamF32s).push(\(isSomeVar) ? Math.fround(\(value)) : 0.0);" + ) + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + case .double: + printer.write( + "\(JSGlueVariableScope.reservedTmpParamF64s).push(\(isSomeVar) ? \(value) : 0.0);" + ) + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + default: + printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") + } + return [] } ) @@ -2083,6 +2160,34 @@ struct IntrinsicJSFragment: Sendable { } printer.write("}") + return [optVar] + } + ) + case .undefinedOr(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) = undefined;") + } + printer.write("}") + return [optVar] } ) @@ -2331,6 +2436,8 @@ struct IntrinsicJSFragment: Sendable { return try! arrayLift(elementType: innerElementType) case .optional(let wrappedType): return try optionalElementRaiseFragment(wrappedType: wrappedType) + case .undefinedOr(let wrappedType): + return try optionalElementRaiseFragment(wrappedType: wrappedType, absenceLiteral: "undefined") case .unsafePointer: return IntrinsicJSFragment( parameters: [], @@ -2458,6 +2565,14 @@ struct IntrinsicJSFragment: Sendable { return [] } ) + case .undefinedOr: + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanup in + printer.write("throw new Error(\"Unsupported array element type for lowering: \(elementType)\");") + return [] + } + ) case .swiftHeapObject: return IntrinsicJSFragment( parameters: ["value"], @@ -2479,6 +2594,8 @@ struct IntrinsicJSFragment: Sendable { ) case .array(let innerElementType): return try! arrayLower(elementType: innerElementType) + case .undefinedOr: + throw BridgeJSLinkError(message: "Unsupported array element type: \(elementType)") case .optional(let wrappedType): return try optionalElementLowerFragment(wrappedType: wrappedType) case .swiftProtocol: @@ -2506,7 +2623,10 @@ struct IntrinsicJSFragment: Sendable { } } - private static func optionalElementRaiseFragment(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + private static func optionalElementRaiseFragment( + wrappedType: BridgeType, + absenceLiteral: String = "null" + ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: [], printCode: { arguments, scope, printer, cleanup in @@ -2517,7 +2637,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("let \(resultVar);") printer.write("if (\(isSomeVar) === 0) {") printer.indent { - printer.write("\(resultVar) = null;") + printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("} else {") printer.indent { @@ -2536,14 +2656,18 @@ struct IntrinsicJSFragment: Sendable { ) } - private static func optionalElementLowerFragment(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + private static func optionalElementLowerFragment( + wrappedType: BridgeType, + presenceCheck: (@Sendable (String) -> String)? = nil + ) throws -> IntrinsicJSFragment { 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 ? 1 : 0;") + let presenceExpr = presenceCheck?(value) ?? "\(value) != null" + printer.write("const \(isSomeVar) = \(presenceExpr) ? 1 : 0;") // Cleanup is written inside the if block so retained id is in scope let localCleanupWriter = CodeFragmentPrinter() printer.write("if (\(isSomeVar)) {") @@ -3095,6 +3219,14 @@ struct IntrinsicJSFragment: Sendable { } } ) + case .undefinedOr: + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, scope, printer, cleanup in + printer.write("throw new Error(\"Unsupported struct field type for lowering: \(field.type)\");") + return [] + } + ) case .swiftStruct(let nestedName): return IntrinsicJSFragment( parameters: ["value"], @@ -3451,6 +3583,14 @@ struct IntrinsicJSFragment: Sendable { return [varName] } ) + case .undefinedOr: + return IntrinsicJSFragment( + parameters: [], + printCode: { arguments, scope, printer, cleanup in + printer.write("throw new Error(\"Unsupported struct field type: \(field.type)\");") + return [] + } + ) case .void, .swiftProtocol, .namespaceEnum, .closure: // These types should not appear as struct fields return IntrinsicJSFragment( diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 5013dda6..fabff5d3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -130,6 +130,7 @@ public enum BridgeType: Codable, Equatable, Hashable, Sendable { case int, uint, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void case unsafePointer(UnsafePointerType) indirect case optional(BridgeType) + indirect case undefinedOr(BridgeType) indirect case array(BridgeType) case caseEnum(String) case rawValueEnum(String, SwiftEnumRawType) @@ -895,7 +896,7 @@ extension BridgeType { return .pointer case .unsafePointer: return .pointer - case .optional(_): + case .optional(_), .undefinedOr(_): return nil case .caseEnum: return .i32 @@ -923,6 +924,7 @@ extension BridgeType { /// Returns true if this type is optional public var isOptional: Bool { if case .optional = self { return true } + if case .undefinedOr = self { return true } return false } @@ -961,6 +963,8 @@ extension BridgeType { return kindCode case .optional(let wrapped): return "Sq\(wrapped.mangleTypeName)" + case .undefinedOr(let wrapped): + return "Su\(wrapped.mangleTypeName)" case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name), @@ -987,7 +991,15 @@ extension BridgeType { /// Side channels are needed when the wrapped type cannot be directly returned via WASM, /// or when we need to distinguish null from absent value for certain primitives. public func usesSideChannelForOptionalReturn() -> Bool { - guard case .optional(let wrappedType) = self else { return false } + let wrappedType: BridgeType + switch self { + case .optional(let wrapped): + wrappedType = wrapped + case .undefinedOr(let wrapped): + wrappedType = wrapped + default: + return false + } switch wrappedType { case .string, .int, .float, .double, .jsObject, .swiftProtocol: diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js index 258334ee..fd31967b 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js @@ -676,6 +676,30 @@ export class TypeProcessor { * @returns {string} */ const convert = (type) => { + // Handle nullable/undefined unions (e.g. T | null, T | undefined) + const isUnionType = (type.flags & ts.TypeFlags.Union) !== 0; + if (isUnionType) { + /** @type {ts.UnionType} */ + // @ts-ignore + const unionType = type; + const unionTypes = unionType.types; + const hasNull = unionTypes.some(t => (t.flags & ts.TypeFlags.Null) !== 0); + const hasUndefined = unionTypes.some(t => (t.flags & ts.TypeFlags.Undefined) !== 0); + const nonNullableTypes = unionTypes.filter( + t => (t.flags & ts.TypeFlags.Null) === 0 && (t.flags & ts.TypeFlags.Undefined) === 0 + ); + if (nonNullableTypes.length === 1 && (hasNull || hasUndefined)) { + const wrapped = this.visitType(nonNullableTypes[0], node); + if (hasNull && hasUndefined) { + return `JSUndefinedOr>`; + } + if (hasNull) { + return `Optional<${wrapped}>`; + } + return `JSUndefinedOr<${wrapped}>`; + } + } + /** @type {Record} */ const typeMap = { "number": "Double", diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 7b2d97f0..ac60ca86 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -1015,11 +1015,6 @@ extension UnsafeMutablePointer: _BridgedSwiftStackType { } extension Optional where Wrapped == Bool { - // MARK: ImportTS - - @available(*, unavailable, message: "Optional Bool type is not supported to be passed to imported JS functions") - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Void {} - // MARK: ExportSwift @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> ( @@ -1071,11 +1066,6 @@ extension Optional where Wrapped == Bool { } extension Optional where Wrapped == Int { - // MARK: ImportTS - - @available(*, unavailable, message: "Optional Int type is not supported to be passed to imported JS functions") - @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Void {} - // MARK: ExportSwift @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> ( @@ -1123,14 +1113,7 @@ extension Optional where Wrapped == Int { } extension Optional where Wrapped == UInt { - // MARK: ImportTS - - @available(*, unavailable, message: "Optional UInt type is not supported to be passed to imported JS functions") - @_spi(BridgeJS) public func bridgeJSLowerParameter() -> Void {} - - // MARK: ExportSwift - - @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameterWithPresence() -> ( + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> ( isSome: Int32, value: Int32 ) { switch consume self { diff --git a/Sources/JavaScriptKit/JSUndefinedOr.swift b/Sources/JavaScriptKit/JSUndefinedOr.swift new file mode 100644 index 00000000..f70cfc0d --- /dev/null +++ b/Sources/JavaScriptKit/JSUndefinedOr.swift @@ -0,0 +1,408 @@ +@frozen public enum JSUndefinedOr { + case undefined + case value(Wrapped) + + /// Convenience accessor for the undefined case. + public static var undefinedValue: Self { .undefined } + + @inlinable + init(optional: Wrapped?) { + self = optional.map(Self.value) ?? .undefined + } + + @inlinable + var optionalRepresentation: Wrapped? { + switch self { + case .undefined: + return nil + case .value(let wrapped): + return wrapped + } + } +} + +extension JSUndefinedOr: ConstructibleFromJSValue where Wrapped: ConstructibleFromJSValue { + public static func construct(from value: JSValue) -> Self? { + if value.isUndefined { return .undefined } + guard let wrapped = Wrapped.construct(from: value) else { return nil } + return .value(wrapped) + } +} + +extension JSUndefinedOr: ConvertibleToJSValue where Wrapped: ConvertibleToJSValue { + public var jsValue: JSValue { + switch self { + case .undefined: + return .undefined + case .value(let wrapped): + return wrapped.jsValue + } + } +} + +// MARK: - BridgeJS Optional-style conformances + +extension JSUndefinedOr where Wrapped == Bool { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ value: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(value)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped == Int { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped == UInt { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped == String { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ bytes: Int32, + _ count: Int32 + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped == JSObject { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftProtocolWrapper { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftHeapObject { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, pointer: UnsafeMutableRawPointer) + { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameterWithRetain() -> ( + isSome: Int32, pointer: UnsafeMutableRawPointer + ) { + optionalRepresentation.bridgeJSLowerParameterWithRetain() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ pointer: UnsafeMutableRawPointer + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, pointer)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } +} + +extension JSUndefinedOr where Wrapped == Float { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped == Double { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(caseId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == String { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ bytes: Int32, + _ count: Int32 + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Int { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Bool { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Float { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Double { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, caseId: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(caseId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension JSUndefinedOr where Wrapped: _BridgedSwiftStruct { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index a20ea2dd..007f0b26 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -611,6 +611,29 @@ typealias OptionalAge = Int? @JS func roundTripOptionalClass(value: Greeter?) -> Greeter? { return value } + +@JS func roundTripOptionalGreeter(_ value: Greeter?) -> Greeter? { + value +} + +@JS func applyOptionalGreeter(_ value: Greeter?, _ transform: (Greeter?) -> Greeter?) -> Greeter? { + transform(value) +} + +@JS class OptionalHolder { + @JS var nullableGreeter: Greeter? + @JS var undefinedNumber: JSUndefinedOr + + @JS init(nullableGreeter: Greeter?, undefinedNumber: JSUndefinedOr) { + self.nullableGreeter = nullableGreeter + self.undefinedNumber = undefinedNumber + } +} + +@JS func makeOptionalHolder(nullableGreeter: Greeter?, undefinedNumber: JSUndefinedOr) -> OptionalHolder { + OptionalHolder(nullableGreeter: nullableGreeter, undefinedNumber: undefinedNumber) +} + @JS class OptionalPropertyHolder { @JS var optionalName: String? @JS var optionalAge: Int? = nil diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift index e066ad27..ee0455c3 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift @@ -14,6 +14,10 @@ @JSFunction func jsRoundTripString(_ v: String) throws(JSException) -> String +@JSFunction func jsRoundTripOptionalNumberNull(_ v: Optional) throws(JSException) -> Optional + +@JSFunction func jsRoundTripOptionalNumberUndefined(_ v: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + @JSFunction func jsThrowOrVoid(_ shouldThrow: Bool) throws(JSException) -> Void @JSFunction func jsThrowOrNumber(_ shouldThrow: Bool) throws(JSException) -> Double diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 94359a70..a572f4c6 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -679,6 +679,54 @@ public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7 #endif } +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer +#else +fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ callback: Int32, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + fatalError("Only available on WebAssembly") +} +#endif + +private final class _BJS_ClosureBox_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC: _BridgedSwiftClosureBox { + let closure: (Optional) -> Optional + init(_ closure: @escaping (Optional) -> Optional) { + self.closure = closure + } +} + +private enum _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC { + static func bridgeJSLower(_ closure: @escaping (Optional) -> Optional) -> UnsafeMutableRawPointer { + let box = _BJS_ClosureBox_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(closure) + return Unmanaged.passRetained(box).toOpaque() + } + static func bridgeJSLift(_ callbackId: Int32) -> (Optional) -> Optional { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let (param0IsSome, param0Pointer) = param0.bridgeJSLowerParameter() + let ret = invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(callbackValue, param0IsSome, param0Pointer) + return Optional.bridgeJSLiftReturn(ret) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +@_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +@_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC") +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let box = Unmanaged<_BJS_ClosureBox_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC>.fromOpaque(boxPtr).takeUnretainedValue() + let result = box.closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + return result.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + #if arch(wasm32) @_extern(wasm, module: "bjs", name: "invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS") fileprivate func invoke_js_callback_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq9APIResultO_SS(_ callback: Int32, _ param0IsSome: Int32, _ param0CaseId: Int32) -> Int32 @@ -4416,6 +4464,39 @@ public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valueValue: Unsa #endif } +@_expose(wasm, "bjs_roundTripOptionalGreeter") +@_cdecl("bjs_roundTripOptionalGreeter") +public func _bjs_roundTripOptionalGreeter(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = roundTripOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_applyOptionalGreeter") +@_cdecl("bjs_applyOptionalGreeter") +public func _bjs_applyOptionalGreeter(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer, _ transform: Int32) -> Void { + #if arch(wasm32) + let ret = applyOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valueValue), _: _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC.bridgeJSLift(transform)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeOptionalHolder") +@_cdecl("bjs_makeOptionalHolder") +public func _bjs_makeOptionalHolder(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsDefined: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = makeOptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsDefined, undefinedNumberValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripOptionalAPIOptionalResult") @_cdecl("bjs_roundTripOptionalAPIOptionalResult") public func _bjs_roundTripOptionalAPIOptionalResult(_ resultIsSome: Int32, _ resultCaseId: Int32) -> Void { @@ -5826,6 +5907,84 @@ fileprivate func _bjs_TestServer_wrap(_ pointer: UnsafeMutableRawPointer) -> Int } #endif +@_expose(wasm, "bjs_OptionalHolder_init") +@_cdecl("bjs_OptionalHolder_init") +public func _bjs_OptionalHolder_init(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsDefined: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = OptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsDefined, undefinedNumberValue)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalHolder_nullableGreeter_get") +@_cdecl("bjs_OptionalHolder_nullableGreeter_get") +public func _bjs_OptionalHolder_nullableGreeter_get(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalHolder.bridgeJSLiftParameter(_self).nullableGreeter + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalHolder_nullableGreeter_set") +@_cdecl("bjs_OptionalHolder_nullableGreeter_set") +public func _bjs_OptionalHolder_nullableGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + OptionalHolder.bridgeJSLiftParameter(_self).nullableGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalHolder_undefinedNumber_get") +@_cdecl("bjs_OptionalHolder_undefinedNumber_get") +public func _bjs_OptionalHolder_undefinedNumber_get(_ _self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = OptionalHolder.bridgeJSLiftParameter(_self).undefinedNumber + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalHolder_undefinedNumber_set") +@_cdecl("bjs_OptionalHolder_undefinedNumber_set") +public func _bjs_OptionalHolder_undefinedNumber_set(_ _self: UnsafeMutableRawPointer, _ valueIsDefined: Int32, _ valueValue: Float64) -> Void { + #if arch(wasm32) + OptionalHolder.bridgeJSLiftParameter(_self).undefinedNumber = JSUndefinedOr.bridgeJSLiftParameter(valueIsDefined, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_OptionalHolder_deinit") +@_cdecl("bjs_OptionalHolder_deinit") +public func _bjs_OptionalHolder_deinit(_ pointer: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(pointer).release() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension OptionalHolder: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + return .object(JSObject(id: UInt32(bitPattern: _bjs_OptionalHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_OptionalHolder_wrap") +fileprivate func _bjs_OptionalHolder_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 +#else +fileprivate func _bjs_OptionalHolder_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + @_expose(wasm, "bjs_OptionalPropertyHolder_init") @_cdecl("bjs_OptionalPropertyHolder_init") public func _bjs_OptionalPropertyHolder_init(_ optionalNameIsSome: Int32, _ optionalNameBytes: Int32, _ optionalNameLength: Int32) -> UnsafeMutableRawPointer { @@ -7855,6 +8014,42 @@ func _$jsRoundTripString(_ v: String) throws(JSException) -> String { return String.bridgeJSLiftReturn(ret) } +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalNumberNull") +fileprivate func bjs_jsRoundTripOptionalNumberNull(_ vIsSome: Int32, _ vValue: Float64) -> Void +#else +fileprivate func bjs_jsRoundTripOptionalNumberNull(_ vIsSome: Int32, _ vValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripOptionalNumberNull(_ v: Optional) throws(JSException) -> Optional { + let (vIsSome, vValue) = v.bridgeJSLowerParameter() + bjs_jsRoundTripOptionalNumberNull(vIsSome, vValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +#if arch(wasm32) +@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalNumberUndefined") +fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsDefined: Int32, _ vValue: Float64) -> Void +#else +fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsDefined: Int32, _ vValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$jsRoundTripOptionalNumberUndefined(_ v: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { + let (vIsDefined, vValue) = v.bridgeJSLowerParameter() + bjs_jsRoundTripOptionalNumberUndefined(vIsDefined, vValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsThrowOrVoid") fileprivate func bjs_jsThrowOrVoid(_ shouldThrow: Int32) -> Void diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 4b8ac7c7..3c71881a 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -525,6 +525,79 @@ ], "swiftCallName" : "Internal.TestServer" }, + { + "constructor" : { + "abiName" : "bjs_OptionalHolder_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "nullableGreeter", + "name" : "nullableGreeter", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "label" : "undefinedNumber", + "name" : "undefinedNumber", + "type" : { + "undefinedOr" : { + "_0" : { + "double" : { + + } + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "OptionalHolder", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "nullableGreeter", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "undefinedNumber", + "type" : { + "undefinedOr" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "swiftCallName" : "OptionalHolder" + }, { "constructor" : { "abiName" : "bjs_OptionalPropertyHolder_init", @@ -7444,6 +7517,148 @@ } } }, + { + "abiName" : "bjs_roundTripOptionalGreeter", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripOptionalGreeter", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "abiName" : "bjs_applyOptionalGreeter", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "applyOptionalGreeter", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "label" : "_", + "name" : "transform", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC", + "moduleName" : "BridgeJSRuntimeTests", + "parameters" : [ + { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "abiName" : "bjs_makeOptionalHolder", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "makeOptionalHolder", + "parameters" : [ + { + "label" : "nullableGreeter", + "name" : "nullableGreeter", + "type" : { + "optional" : { + "_0" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + } + }, + { + "label" : "undefinedNumber", + "name" : "undefinedNumber", + "type" : { + "undefinedOr" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "OptionalHolder" + } + } + }, { "abiName" : "bjs_roundTripOptionalAPIOptionalResult", "effects" : { @@ -11739,6 +11954,58 @@ } } }, + { + "name" : "jsRoundTripOptionalNumberNull", + "parameters" : [ + { + "name" : "v", + "type" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "double" : { + + } + } + } + } + }, + { + "name" : "jsRoundTripOptionalNumberUndefined", + "parameters" : [ + { + "name" : "v", + "type" : { + "undefinedOr" : { + "_0" : { + "double" : { + + } + } + } + } + } + ], + "returnType" : { + "undefinedOr" : { + "_0" : { + "double" : { + + } + } + } + } + }, { "name" : "jsThrowOrVoid", "parameters" : [ diff --git a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift index ea9f8c68..136bd36f 100644 --- a/Tests/BridgeJSRuntimeTests/ImportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ImportAPITests.swift @@ -35,6 +35,26 @@ class ImportAPITests: XCTestCase { } } + func testRoundTripOptionalNumberNull() throws { + try XCTAssertEqual(jsRoundTripOptionalNumberNull(42), 42) + try XCTAssertNil(jsRoundTripOptionalNumberNull(nil)) + } + + func testRoundTripOptionalNumberUndefined() throws { + let some = try jsRoundTripOptionalNumberUndefined(.value(42)) + switch some { + case .value(let value): + XCTAssertEqual(value, 42) + case .undefined: + XCTFail("Expected defined value") + } + + let undefined = try jsRoundTripOptionalNumberUndefined(.undefinedValue) + if case .value = undefined { + XCTFail("Expected undefined") + } + } + func testRoundTripFeatureFlag() throws { for v in [FeatureFlag.foo, .bar] { try XCTAssertEqual(jsRoundTripFeatureFlag(v), v) diff --git a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts index 983d6052..d1140e20 100644 --- a/Tests/BridgeJSRuntimeTests/bridge-js.d.ts +++ b/Tests/BridgeJSRuntimeTests/bridge-js.d.ts @@ -2,6 +2,8 @@ export function jsRoundTripVoid(): void export function jsRoundTripNumber(v: number): number export function jsRoundTripBool(v: boolean): boolean export function jsRoundTripString(v: string): string +export function jsRoundTripOptionalNumberNull(v: number | null): number | null +export function jsRoundTripOptionalNumberUndefined(v: number | undefined): number | undefined export function jsThrowOrVoid(shouldThrow: boolean): void export function jsThrowOrNumber(shouldThrow: boolean): number export function jsThrowOrBool(shouldThrow: boolean): boolean diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 850b31b6..29931ba3 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -25,6 +25,12 @@ export async function setupOptions(options, context) { "jsRoundTripString": (v) => { return v; }, + "jsRoundTripOptionalNumberNull": (v) => { + return v ?? null; + }, + "jsRoundTripOptionalNumberUndefined": (v) => { + return v === undefined ? undefined : v; + }, "jsThrowOrVoid": (shouldThrow) => { if (shouldThrow) { throw new Error("TestError"); @@ -690,6 +696,26 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { testPropertyGreeter.release(); optionalsHolder.release(); + const optGreeter = new exports.Greeter("Optionaly"); + assert.equal(exports.roundTripOptionalGreeter(null), null); + const optGreeterReturned = exports.roundTripOptionalGreeter(optGreeter); + assert.equal(optGreeterReturned.name, "Optionaly"); + assert.equal(optGreeterReturned.greet(), "Hello, Optionaly!"); + + const appliedOptional = exports.applyOptionalGreeter(null, (g) => g ?? optGreeter); + assert.equal(appliedOptional.name, "Optionaly"); + + const holderOpt = exports.makeOptionalHolder(null, undefined); + assert.equal(holderOpt.nullableGreeter, null); + assert.equal(holderOpt.undefinedNumber, undefined); + holderOpt.nullableGreeter = optGreeter; + holderOpt.undefinedNumber = 123.5; + assert.equal(holderOpt.nullableGreeter.name, "Optionaly"); + assert.equal(holderOpt.undefinedNumber, 123.5); + holderOpt.release(); + optGreeterReturned.release(); + optGreeter.release(); + const aor1 = { tag: APIOptionalResultValues.Tag.Success, param0: "hello world" }; const aor2 = { tag: APIOptionalResultValues.Tag.Success, param0: null }; const aor3 = { tag: APIOptionalResultValues.Tag.Failure, param0: 404, param1: true }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..c13ef64e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "strict": true, + "skipLibCheck": true + } +} From 6a230daddf1cb158878b35b63bf6c7cf4568f9ee Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 13:33:30 +0900 Subject: [PATCH 2/6] BridgeJS: Simplify Optional and JSUndefinedOr code paths --- .../Generated/JavaScript/BridgeJS.json | 110 ++- .../Sources/BridgeJSCore/ClosureCodegen.swift | 2 +- .../Sources/BridgeJSCore/ExportSwift.swift | 98 +-- .../Sources/BridgeJSCore/ImportTS.swift | 12 +- .../BridgeJSCore/SwiftToSkeleton.swift | 32 +- .../Sources/BridgeJSLink/BridgeJSLink.swift | 21 +- .../Sources/BridgeJSLink/JSGlueGen.swift | 206 +---- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 47 +- .../BridgeJSCodegenTests/ArrayTypes.json | 70 +- .../CrossFileTypeResolution.ReverseOrder.json | 5 +- .../CrossFileTypeResolution.json | 5 +- .../DefaultParameters.json | 50 +- .../EnumAssociatedValue.json | 95 +- .../BridgeJSCodegenTests/EnumCase.json | 20 +- .../BridgeJSCodegenTests/EnumRawType.json | 120 ++- .../ImportedTypeInExportedInterface.json | 15 +- .../BridgeJSCodegenTests/Optionals.json | 190 ++-- .../BridgeJSCodegenTests/Protocol.json | 40 +- .../StaticProperties.Global.json | 5 +- .../StaticProperties.json | 5 +- .../BridgeJSCodegenTests/SwiftClosure.json | 45 +- .../BridgeJSCodegenTests/SwiftStruct.json | 40 +- .../JavaScriptKit/BridgeJSIntrinsics.swift | 381 ++++++++ Sources/JavaScriptKit/JSUndefinedOr.swift | 371 +------- .../Generated/BridgeJS.swift | 20 +- .../Generated/JavaScript/BridgeJS.json | 825 +++++++++++------- 26 files changed, 1522 insertions(+), 1308 deletions(-) diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json index cb081ab3..4dff9713 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json @@ -449,12 +449,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -470,12 +471,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -491,12 +493,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, @@ -512,12 +515,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, @@ -533,12 +537,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -554,12 +559,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -575,12 +581,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -596,12 +603,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -1844,12 +1852,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -1876,12 +1885,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -1902,12 +1912,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -1917,12 +1928,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -1943,12 +1955,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -1975,12 +1988,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -2001,12 +2015,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -2016,12 +2031,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -2040,7 +2056,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -2049,7 +2065,8 @@ } } } - } + }, + "_1" : "null" } } } @@ -2072,7 +2089,7 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -2081,7 +2098,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -2097,7 +2115,7 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -2106,7 +2124,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -2123,7 +2142,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -2132,13 +2151,14 @@ } } } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -2147,7 +2167,8 @@ } } } - } + }, + "_1" : "null" } } } @@ -2601,12 +2622,13 @@ "isStatic" : false, "name" : "email", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index 5d654566..30cf8b2c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -22,7 +22,7 @@ public struct ClosureCodegen { collectClosureSignatures(from: paramType, into: &signatures) } collectClosureSignatures(from: signature.returnType, into: &signatures) - case .optional(let wrapped): + case .nullable(let wrapped, _): collectClosureSignatures(from: wrapped, into: &signatures) default: break diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 39281ddf..cb267c21 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -133,17 +133,27 @@ public class ExportSwift { case .array: typeNameForIntrinsic = param.type.swiftType liftingExpr = StackCodegen().liftExpression(for: param.type) - case .optional(let wrappedType): + case .nullable(let wrappedType, let kind): + let optionalSwiftType: String + if case .null = kind { + optionalSwiftType = "Optional" + } else { + optionalSwiftType = "JSUndefinedOr" + } if case .array(let elementType) = wrappedType { let arrayLift = StackCodegen().liftArrayExpression(elementType: elementType) let isSomeParam = argumentsToLift[0] let swiftTypeName = elementType.swiftType - typeNameForIntrinsic = "Optional<[\(swiftTypeName)]>" + typeNameForIntrinsic = "\(optionalSwiftType)<[\(swiftTypeName)]>" + let absentExpr = + kind == .null + ? "\(optionalSwiftType)<[\(swiftTypeName)]>.none" + : "\(optionalSwiftType)<[\(swiftTypeName)]>.undefinedValue" liftingExpr = ExprSyntax( """ { if \(raw: isSomeParam) == 0 { - return Optional<[\(raw: swiftTypeName)]>.none + return \(raw: absentExpr) } else { return \(arrayLift) } @@ -152,12 +162,12 @@ public class ExportSwift { ) } else if case .swiftProtocol(let protocolName) = wrappedType { let wrapperName = "Any\(protocolName)" - typeNameForIntrinsic = "Optional<\(wrapperName)>" + typeNameForIntrinsic = "\(optionalSwiftType)<\(wrapperName)>" liftingExpr = ExprSyntax( "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" ) } else { - typeNameForIntrinsic = "Optional<\(wrappedType.swiftType)>" + typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.swiftType)>" liftingExpr = ExprSyntax( "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" ) @@ -220,7 +230,7 @@ public class ExportSwift { return CodeBlockItemSyntax( item: .init(DeclSyntax("let ret = \(raw: callExpr) as! \(raw: wrapperName)")) ) - case .optional(let wrappedType): + case .nullable(let wrappedType, _): if case .swiftProtocol(let protocolName) = wrappedType { let wrapperName = "Any\(protocolName)" return CodeBlockItemSyntax( @@ -251,7 +261,7 @@ public class ExportSwift { case .swiftProtocol(let protocolName): let wrapperName = "Any\(protocolName)" append("let ret = \(raw: name) as! \(raw: wrapperName)") - case .optional(let wrappedType): + case .nullable(let wrappedType, _): if case .swiftProtocol(let protocolName) = wrappedType { let wrapperName = "Any\(protocolName)" append("let ret = \(raw: name).flatMap { $0 as? \(raw: wrapperName) }") @@ -278,8 +288,8 @@ public class ExportSwift { private func generateParameterLifting() { let stackParamIndices = parameters.enumerated().compactMap { index, param -> Int? in switch param.type { - case .swiftStruct, .optional(.swiftStruct), - .associatedValueEnum, .optional(.associatedValueEnum), + case .swiftStruct, .nullable(.swiftStruct, _), + .associatedValueEnum, .nullable(.associatedValueEnum, _), .array: return index default: @@ -308,7 +318,7 @@ public class ExportSwift { case .swiftProtocol(let protocolName): let wrapperName = "Any\(protocolName)" append("let ret = \(raw: selfExpr).\(raw: propertyName) as! \(raw: wrapperName)") - case .optional(let wrappedType): + case .nullable(let wrappedType, _): if case .swiftProtocol(let protocolName) = wrappedType { let wrapperName = "Any\(protocolName)" append("let ret = \(raw: selfExpr).\(raw: propertyName).flatMap { $0 as? \(raw: wrapperName) }") @@ -356,7 +366,7 @@ public class ExportSwift { switch returnType { case .closure(let signature): append("return _BJS_Closure_\(raw: signature.mangleName).bridgeJSLower(ret)") - case .array, .optional(.array): + case .array, .nullable(.array, _): let stackCodegen = StackCodegen() for stmt in stackCodegen.lowerStatements(for: returnType, accessor: "ret", varPrefix: "ret") { append(stmt) @@ -818,10 +828,8 @@ struct StackCodegen { } case .associatedValueEnum: return "\(raw: type.swiftType).bridgeJSLiftParameter(_swift_js_pop_i32())" - case .optional(let wrappedType): - return liftOptionalExpression(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): - return liftUndefinedExpression(wrappedType: wrappedType) + case .nullable(let wrappedType, let kind): + return liftNullableExpression(wrappedType: wrappedType, kind: kind) case .array(let elementType): return liftArrayExpression(elementType: elementType) case .closure: @@ -841,7 +849,7 @@ struct StackCodegen { return liftArrayExpressionInline(elementType: elementType) case .swiftProtocol(let protocolName): return "[Any\(raw: protocolName)].bridgeJSLiftParameter()" - case .optional, .undefinedOr, .array, .closure: + case .nullable, .array, .closure: return liftArrayExpressionInline(elementType: elementType) case .void, .namespaceEnum: fatalError("Invalid array element type: \(elementType)") @@ -865,51 +873,32 @@ struct StackCodegen { """ } - private func liftOptionalExpression(wrappedType: BridgeType) -> ExprSyntax { + private func liftNullableExpression(wrappedType: BridgeType, kind: JSOptionalKind) -> ExprSyntax { + let typeName = kind == .null ? "Optional" : "JSUndefinedOr" switch wrappedType { case .string, .int, .uint, .bool, .float, .double, .jsObject(nil), .swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum: - return "Optional<\(raw: wrappedType.swiftType)>.bridgeJSLiftParameter()" + return "\(raw: typeName)<\(raw: wrappedType.swiftType)>.bridgeJSLiftParameter()" case .jsObject(let className?): - return "Optional.bridgeJSLiftParameter().map { \(raw: className)(unsafelyWrapping: $0) }" + return "\(raw: typeName).bridgeJSLiftParameter().map { \(raw: className)(unsafelyWrapping: $0) }" case .array(let elementType): let arrayLift = liftArrayExpression(elementType: elementType) let swiftTypeName = elementType.swiftType + let absentExpr = + kind == .null + ? "\(typeName)<[\(swiftTypeName)]>.none" : "\(typeName)<[\(swiftTypeName)]>.undefinedValue" return """ { let __isSome = _swift_js_pop_i32() if __isSome == 0 { - return Optional<[\(raw: swiftTypeName)]>.none + return \(raw: absentExpr) } else { return \(arrayLift) } }() """ - case .undefinedOr, .void, .namespaceEnum, .closure, .optional, .unsafePointer, .swiftProtocol: - fatalError("Invalid optional wrapped type: \(wrappedType)") - } - } - - private func liftUndefinedExpression(wrappedType: BridgeType) -> ExprSyntax { - switch wrappedType { - case .string, .int, .uint, .bool, .float, .double, .jsObject, - .swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum: - return "JSUndefinedOr<\(raw: wrappedType.swiftType)>.bridgeJSLiftParameter()" - case .array(let elementType): - let arrayLift = liftArrayExpression(elementType: elementType) - let swiftTypeName = elementType.swiftType - return """ - { - let __isDefined = _swift_js_pop_i32() - if __isDefined == 0 { - return JSUndefinedOr<\(raw: swiftTypeName)>.undefined - } else { - return JSUndefinedOr<\(raw: swiftTypeName)>(optional: \(arrayLift)) - } - }() - """ - case .void, .namespaceEnum, .closure, .optional, .undefinedOr, .unsafePointer, .swiftProtocol: - fatalError("Invalid undefinedOr wrapped type: \(wrappedType)") + case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol: + fatalError("Invalid nullable wrapped type: \(wrappedType)") } } @@ -940,9 +929,7 @@ struct StackCodegen { return ["\(raw: accessor).bridgeJSLowerStackReturn()"] case .associatedValueEnum, .swiftStruct: return ["\(raw: accessor).bridgeJSLowerReturn()"] - case .optional(let wrappedType): - return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, _): return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix) case .void, .namespaceEnum: return [] @@ -965,7 +952,7 @@ struct StackCodegen { return ["\(raw: accessor).map { $0.jsObject }.bridgeJSLowerReturn()"] case .swiftProtocol(let protocolName): return ["\(raw: accessor).map { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"] - case .optional, .undefinedOr, .array, .closure: + case .nullable, .array, .closure: return lowerArrayStatementsInline( elementType: elementType, accessor: accessor, @@ -1623,8 +1610,8 @@ extension BridgeType { case .unsafePointer(let ptr): return ptr.swiftType case .swiftProtocol(let name): return "Any\(name)" case .void: return "Void" - case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>" - case .undefinedOr(let wrappedType): return "JSUndefinedOr<\(wrappedType.swiftType)>" + case .nullable(let wrappedType, let kind): + return kind == .null ? "Optional<\(wrappedType.swiftType)>" : "JSUndefinedOr<\(wrappedType.swiftType)>" case .array(let elementType): return "[\(elementType.swiftType)]" case .caseEnum(let name): return name case .rawValueEnum(let name, _): return name @@ -1668,14 +1655,10 @@ extension BridgeType { case .unsafePointer: return .unsafePointer case .swiftProtocol: return .jsObject case .void: return .void - case .optional(let wrappedType): + case .nullable(let wrappedType, _): var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters) return LiftingIntrinsicInfo(parameters: optionalParams) - case .undefinedOr(let wrappedType): - var params: [(name: String, type: WasmCoreType)] = [("isDefined", .i32)] - params.append(contentsOf: try wrappedType.liftParameterInfo().parameters) - return LiftingIntrinsicInfo(parameters: params) case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): return rawType.liftingIntrinsicInfo @@ -1724,8 +1707,7 @@ extension BridgeType { case .unsafePointer: return .unsafePointer case .swiftProtocol: return .jsObject case .void: return .void - case .optional: return .optional - case .undefinedOr: return .optional + case .nullable: return .optional case .caseEnum: return .caseEnum case .rawValueEnum(_, let rawType): return rawType.loweringIntrinsicInfo diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 7ac72cff..cd3f8444 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -924,16 +924,11 @@ extension BridgeType { } case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as parameters") - case .optional(let wrappedType): + case .nullable(let wrappedType, _): let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) var params = [("isSome", WasmCoreType.i32)] params.append(contentsOf: wrappedInfo.loweredParameters) return LoweringParameterInfo(loweredParameters: params) - case .undefinedOr(let wrappedType): - let wrappedInfo = try wrappedType.loweringParameterInfo(context: context) - var params = [("isDefined", WasmCoreType.i32)] - params.append(contentsOf: wrappedInfo.loweredParameters) - return LoweringParameterInfo(loweredParameters: params) case .array: switch context { case .importTS: @@ -1018,10 +1013,7 @@ extension BridgeType { } case .namespaceEnum: throw BridgeJSCoreError("Namespace enums cannot be used as return values") - case .optional(let wrappedType): - let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) - return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, _): let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) case .array: diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index 67f1421a..adc49c89 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -130,7 +130,7 @@ public final class SwiftToSkeleton { if let optionalType = type.as(OptionalTypeSyntax.self) { let wrappedType = optionalType.wrappedType if let baseType = lookupType(for: wrappedType, errors: &errors) { - return .optional(baseType) + return .nullable(baseType, .null) } } // JSUndefinedOr @@ -141,7 +141,7 @@ public final class SwiftToSkeleton { let argType = TypeSyntax(genericArgs.first?.argument) { if let baseType = lookupType(for: argType, errors: &errors) { - return .undefinedOr(baseType) + return .nullable(baseType, .undefined) } } // JavaScriptKit.JSUndefinedOr @@ -154,7 +154,7 @@ public final class SwiftToSkeleton { let argType = TypeSyntax(genericArgs.first?.argument) { if let wrappedType = lookupType(for: argType, errors: &errors) { - return .undefinedOr(wrappedType) + return .nullable(wrappedType, .undefined) } } // Optional @@ -165,7 +165,7 @@ public final class SwiftToSkeleton { let argType = TypeSyntax(genericArgs.first?.argument) { if let baseType = lookupType(for: argType, errors: &errors) { - return .optional(baseType) + return .nullable(baseType, .null) } } // Swift.Optional @@ -178,7 +178,7 @@ public final class SwiftToSkeleton { let argType = TypeSyntax(genericArgs.first?.argument) { if let wrappedType = lookupType(for: argType, errors: &errors) { - return .optional(wrappedType) + return .nullable(wrappedType, .null) } } // [T] @@ -559,7 +559,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { switch type { case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name): enumName = name - case .optional(let wrappedType): + case .nullable(let wrappedType, _): switch wrappedType { case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name): enumName = name @@ -608,7 +608,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { let expr = defaultClause.value if expr.is(NilLiteralExprSyntax.self) { - guard case .optional(_) = type else { + guard case .nullable = type else { diagnose( node: expr, message: "nil is only valid for optional parameters", @@ -664,10 +664,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { let isStructType: Bool let expectedTypeName: String? switch type { - case .swiftStruct(let name), .optional(.swiftStruct(let name)): + case .swiftStruct(let name), .nullable(.swiftStruct(let name), _): isStructType = true expectedTypeName = name.split(separator: ".").last.map(String.init) - case .swiftHeapObject(let name), .optional(.swiftHeapObject(let name)): + case .swiftHeapObject(let name), .nullable(.swiftHeapObject(let name), _): isStructType = false expectedTypeName = name.split(separator: ".").last.map(String.init) default: @@ -803,7 +803,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { switch type { case .array(let element): elementType = element - case .optional(.array(let element)): + case .nullable(.array(let element), _): elementType = element default: diagnose( @@ -858,11 +858,11 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { continue } } - if case .optional(let wrappedType) = type, wrappedType.isOptional { + if case .nullable(let wrappedType, _) = type, wrappedType.isOptional { diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription) continue } - if case .optional(let wrappedType) = type, wrappedType.isOptional { + if case .nullable(let wrappedType, _) = type, wrappedType.isOptional { diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription) continue } @@ -984,7 +984,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { if let returnClause = node.signature.returnClause { let resolvedType = withLookupErrors { self.parent.lookupType(for: returnClause.type, errors: &$0) } - if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { + if let type = resolvedType, case .nullable(let wrappedType, _) = type, wrappedType.isOptional { diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription) return nil } @@ -1422,7 +1422,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { switch associatedValue.type { case .string, .int, .float, .double, .bool: break - case .optional(let wrappedType): + case .nullable(let wrappedType, _): switch wrappedType { case .string, .int, .float, .double, .bool: break @@ -1611,7 +1611,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { if let returnClause = node.signature.returnClause { let resolvedType = withLookupErrors { self.parent.lookupType(for: returnClause.type, errors: &$0) } - if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional { + if let type = resolvedType, case .nullable(let wrappedType, _) = type, wrappedType.isOptional { diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription) return nil } @@ -1836,7 +1836,7 @@ fileprivate extension BridgeType { switch (self, expectedType) { case let (lhs, rhs) where lhs == rhs: return true - case (.optional(let wrapped), expectedType): + case (.nullable(let wrapped, _), expectedType): return wrapped == expectedType default: return false diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 0b1be8a1..97678dc6 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -297,7 +297,7 @@ public struct BridgeJSLink { switch type { case .closure: return true - case .optional(let wrapped): + case .nullable(let wrapped, _): return containsClosureType(in: wrapped) default: return false @@ -746,7 +746,7 @@ public struct BridgeJSLink { collectClosureSignatures(from: paramType, into: &signatures) } collectClosureSignatures(from: signature.returnType, into: &signatures) - case .optional(let wrapped): + case .nullable(let wrapped, _): collectClosureSignatures(from: wrapped, into: &signatures) default: break @@ -764,7 +764,7 @@ public struct BridgeJSLink { // Build parameter list for invoke function var invokeParams: [String] = ["callbackId"] for (index, paramType) in signature.parameters.enumerated() { - if case .optional = paramType { + if case .nullable = paramType { invokeParams.append("param\(index)IsSome") invokeParams.append("param\(index)Value") } else { @@ -782,7 +782,7 @@ public struct BridgeJSLink { for (index, paramType) in signature.parameters.enumerated() { let fragment = try! IntrinsicJSFragment.closureLiftParameter(type: paramType) let args: [String] - if case .optional = paramType { + if case .nullable = paramType { args = ["param\(index)IsSome", "param\(index)Value", "param\(index)"] } else { args = ["param\(index)Id", "param\(index)"] @@ -1428,8 +1428,9 @@ public struct BridgeJSLink { return type.tsType case .swiftStruct(let name): return name.components(separatedBy: ".").last ?? name - case .optional(let wrapped): - return "\(resolveTypeScriptType(wrapped, exportedSkeletons: exportedSkeletons)) | null" + case .nullable(let wrapped, let kind): + let base = resolveTypeScriptType(wrapped, exportedSkeletons: exportedSkeletons) + return "\(base) | \(kind.absenceLiteral)" case .array(let elementType): let elementTypeStr = resolveTypeScriptType(elementType, exportedSkeletons: exportedSkeletons) // Parenthesize compound types so `[]` binds correctly in TypeScript @@ -2250,7 +2251,7 @@ extension BridgeJSLink { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) if context == .exportSwift, returnType.usesSideChannelForOptionalReturn() { - guard case .optional(let wrappedType) = returnType else { + guard case .nullable(let wrappedType, _) = returnType else { fatalError("usesSideChannelForOptionalReturn returned true for non-optional type") } @@ -3403,10 +3404,8 @@ extension BridgeType { return name case .unsafePointer: return "number" - case .optional(let wrappedType): - return "\(wrappedType.tsType) | null" - case .undefinedOr(let wrappedType): - return "\(wrappedType.tsType) | undefined" + case .nullable(let wrappedType, let kind): + return "\(wrappedType.tsType) | \(kind.absenceLiteral)" case .caseEnum(let name): return "\(name)Tag" case .rawValueEnum(let name, _): diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index ad27dc6f..44e751d3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -298,7 +298,7 @@ struct IntrinsicJSFragment: Sendable { static func optionalLiftParameter( wrappedType: BridgeType, - absenceLiteral: String = "null" + kind: JSOptionalKind ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["isSome", "wrappedValue"], @@ -306,6 +306,7 @@ struct IntrinsicJSFragment: Sendable { let isSome = arguments[0] let wrappedValue = arguments[1] let resultExpr: String + let absenceLiteral = kind.absenceLiteral switch wrappedType { case .int, .float, .double, .caseEnum: @@ -518,8 +519,9 @@ struct IntrinsicJSFragment: Sendable { static func optionalLiftReturn( wrappedType: BridgeType, context: BridgeContext = .exportSwift, - absenceLiteral: String = "null" + kind: JSOptionalKind ) -> IntrinsicJSFragment { + let absenceLiteral = kind.absenceLiteral return IntrinsicJSFragment( parameters: [], printCode: { arguments, scope, printer, cleanupCode in @@ -646,7 +648,7 @@ struct IntrinsicJSFragment: Sendable { presenceCheck: (@Sendable (String) -> String)? = nil ) throws -> IntrinsicJSFragment { switch wrappedType { - case .void, .optional, .namespaceEnum, .closure: + case .void, .nullable, .namespaceEnum, .closure: throw BridgeJSLinkError(message: "Unsupported optional wrapped type for protocol export: \(wrappedType)") default: break } @@ -913,15 +915,19 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .optional(let wrappedType), .undefinedOr(let wrappedType): - return try closureOptionalLiftParameter(wrappedType: wrappedType) + case .nullable(let wrappedType, let kind): + return try closureOptionalLiftParameter(wrappedType: wrappedType, kind: kind) default: throw BridgeJSLinkError(message: "Unsupported closure parameter type for lifting: \(type)") } } - /// Handles optional parameter lifting for closure invocation - private static func closureOptionalLiftParameter(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + /// Handles optional parameter lifting for closure invocation. + private static func closureOptionalLiftParameter( + wrappedType: BridgeType, + kind: JSOptionalKind + ) throws -> IntrinsicJSFragment { + let absenceLiteral = kind.absenceLiteral switch wrappedType { case .string, .rawValueEnum, .int, .bool, .double, .float, .jsObject, .swiftHeapObject, .caseEnum, .associatedValueEnum: @@ -983,7 +989,7 @@ struct IntrinsicJSFragment: Sendable { printer.unindent() printer.write("} else {") printer.indent() - printer.write("\(targetVar) = null;") + printer.write("\(targetVar) = \(absenceLiteral);") printer.unindent() printer.write("}") @@ -1114,7 +1120,7 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .optional(let wrappedType), .undefinedOr(let wrappedType): + case .nullable(let wrappedType, _): return try closureOptionalLowerReturn(wrappedType: wrappedType) default: throw BridgeJSLinkError(message: "Unsupported closure return type for lowering: \(type)") @@ -1309,10 +1315,8 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .optional(let wrappedType): - return try closureOptionalLiftReturn(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): - return try closureOptionalLiftReturn(wrappedType: wrappedType, absenceLiteral: "undefined") + case .nullable(let wrappedType, let kind): + return try closureOptionalLiftReturn(wrappedType: wrappedType, kind: kind) default: throw BridgeJSLinkError(message: "Unsupported closure return type for lifting: \(type)") } @@ -1321,7 +1325,7 @@ struct IntrinsicJSFragment: Sendable { /// Handles optional return lifting for Swift closure returns private static func closureOptionalLiftReturn( wrappedType: BridgeType, - absenceLiteral: String = "null" + kind: JSOptionalKind ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["invokeCall"], @@ -1331,7 +1335,7 @@ struct IntrinsicJSFragment: Sendable { let baseFragment = optionalLiftReturn( wrappedType: wrappedType, context: .importTS, - absenceLiteral: absenceLiteral + kind: kind ) let lifted = baseFragment.printCode([], scope, printer, cleanupCode) if !lifted.isEmpty { @@ -1371,15 +1375,13 @@ struct IntrinsicJSFragment: Sendable { } case .associatedValueEnum: printer.write("return;") - case .optional(let wrappedType): + case .nullable(let wrappedType, _): switch wrappedType { case .swiftHeapObject: printer.write("return 0;") default: printer.write("return;") } - case .undefinedOr: - printer.write("return;") default: printer.write("return 0;") } @@ -1401,9 +1403,7 @@ struct IntrinsicJSFragment: Sendable { return .swiftHeapObjectLowerParameter case .swiftProtocol: return .jsObjectLowerParameter case .void: return .void - case .optional(let wrappedType): - return try .optionalLowerParameter(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, _): return try .optionalLowerParameter(wrappedType: wrappedType) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): @@ -1448,9 +1448,8 @@ struct IntrinsicJSFragment: Sendable { case .unsafePointer: return .identity case .swiftProtocol: return .jsObjectLiftReturn case .void: return .void - case .optional(let wrappedType): return .optionalLiftReturn(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): - return .optionalLiftReturn(wrappedType: wrappedType, absenceLiteral: "undefined") + case .nullable(let wrappedType, let kind): + return .optionalLiftReturn(wrappedType: wrappedType, kind: kind) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1508,10 +1507,8 @@ struct IntrinsicJSFragment: Sendable { throw BridgeJSLinkError( message: "Void can't appear in parameters of imported JS functions" ) - case .optional(let wrappedType): - return try .optionalLiftParameter(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): - return try .optionalLiftParameter(wrappedType: wrappedType, absenceLiteral: "undefined") + case .nullable(let wrappedType, let kind): + return try .optionalLiftParameter(wrappedType: wrappedType, kind: kind) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -1601,12 +1598,10 @@ struct IntrinsicJSFragment: Sendable { } case .swiftProtocol: return .jsObjectLowerReturn case .void: return .void - case .optional(let wrappedType): - return try .optionalLowerReturn(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, let kind): return try .optionalLowerReturn( wrappedType: wrappedType, - presenceCheck: { value in "\(value) !== undefined" } + presenceCheck: { value in kind.presenceCheck(value: value) } ) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): @@ -1956,74 +1951,13 @@ struct IntrinsicJSFragment: Sendable { 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("}") - case .int, .uint: - printer.write( - "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) | 0) : 0);" - ) - printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") - case .bool: - printer.write( - "\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? (\(value) ? 1 : 0) : 0);" - ) - printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") - case .float: - printer.write( - "\(JSGlueVariableScope.reservedTmpParamF32s).push(\(isSomeVar) ? Math.fround(\(value)) : 0.0);" - ) - printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") - case .double: - printer.write( - "\(JSGlueVariableScope.reservedTmpParamF64s).push(\(isSomeVar) ? \(value) : 0.0);" - ) - printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") - default: - printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);") - } - - return [] - } - ) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, let kind): return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, scope, printer, cleanup in let value = arguments[0] let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(value) !== undefined;") + printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") switch wrappedType { case .string: @@ -2135,35 +2069,7 @@ 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] - } - ) - case .undefinedOr(let wrappedType): + case .nullable(let wrappedType, let kind): return IntrinsicJSFragment( parameters: [], printCode: { arguments, scope, printer, cleanup in @@ -2184,7 +2090,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(optVar) = undefined;") + printer.write("\(optVar) = \(kind.absenceLiteral);") } printer.write("}") @@ -2434,10 +2340,8 @@ struct IntrinsicJSFragment: Sendable { ) case .array(let innerElementType): return try! arrayLift(elementType: innerElementType) - case .optional(let wrappedType): - return try optionalElementRaiseFragment(wrappedType: wrappedType) - case .undefinedOr(let wrappedType): - return try optionalElementRaiseFragment(wrappedType: wrappedType, absenceLiteral: "undefined") + case .nullable(let wrappedType, let kind): + return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind) case .unsafePointer: return IntrinsicJSFragment( parameters: [], @@ -2565,14 +2469,6 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .undefinedOr: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, scope, printer, cleanup in - printer.write("throw new Error(\"Unsupported array element type for lowering: \(elementType)\");") - return [] - } - ) case .swiftHeapObject: return IntrinsicJSFragment( parameters: ["value"], @@ -2594,10 +2490,11 @@ struct IntrinsicJSFragment: Sendable { ) case .array(let innerElementType): return try! arrayLower(elementType: innerElementType) - case .undefinedOr: - throw BridgeJSLinkError(message: "Unsupported array element type: \(elementType)") - case .optional(let wrappedType): - return try optionalElementLowerFragment(wrappedType: wrappedType) + case .nullable(let wrappedType, let kind): + return try optionalElementLowerFragment( + wrappedType: wrappedType, + presenceCheck: { kind.presenceCheck(value: $0) } + ) case .swiftProtocol: // Same as jsObject but no cleanup — Swift's AnyProtocol wrapper releases via deinit return IntrinsicJSFragment( @@ -2625,8 +2522,9 @@ struct IntrinsicJSFragment: Sendable { private static func optionalElementRaiseFragment( wrappedType: BridgeType, - absenceLiteral: String = "null" + kind: JSOptionalKind ) throws -> IntrinsicJSFragment { + let absenceLiteral = kind.absenceLiteral return IntrinsicJSFragment( parameters: [], printCode: { arguments, scope, printer, cleanup in @@ -2973,13 +2871,13 @@ struct IntrinsicJSFragment: Sendable { return [idVar] } ) - case .optional(let wrappedType): + case .nullable(let wrappedType, let kind): 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;") + printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") if case .caseEnum = wrappedType { printer.write("if (\(isSomeVar)) {") @@ -3219,14 +3117,6 @@ struct IntrinsicJSFragment: Sendable { } } ) - case .undefinedOr: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, scope, printer, cleanup in - printer.write("throw new Error(\"Unsupported struct field type for lowering: \(field.type)\");") - return [] - } - ) case .swiftStruct(let nestedName): return IntrinsicJSFragment( parameters: ["value"], @@ -3434,7 +3324,7 @@ struct IntrinsicJSFragment: Sendable { return [pVar] } ) - case .optional(let wrappedType): + case .nullable(let wrappedType, let kind): return IntrinsicJSFragment( parameters: [], printCode: { arguments, scope, printer, cleanup in @@ -3472,7 +3362,7 @@ struct IntrinsicJSFragment: Sendable { } printer.write("} else {") printer.indent { - printer.write("\(optVar) = null;") + printer.write("\(optVar) = \(kind.absenceLiteral);") } printer.write("}") return [optVar] @@ -3583,14 +3473,6 @@ struct IntrinsicJSFragment: Sendable { return [varName] } ) - case .undefinedOr: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, scope, printer, cleanup in - printer.write("throw new Error(\"Unsupported struct field type: \(field.type)\");") - return [] - } - ) case .void, .swiftProtocol, .namespaceEnum, .closure: // These types should not appear as struct fields return IntrinsicJSFragment( diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index fabff5d3..caf725ee 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -126,11 +126,32 @@ public struct UnsafePointerType: Codable, Equatable, Hashable, Sendable { } } +/// JS semantics for optional/nullable types: which value represents "absent". +public enum JSOptionalKind: String, Codable, Equatable, Hashable, Sendable { + case null + case undefined + + /// The JS literal for absence (e.g. in generated glue). + public var absenceLiteral: String { + switch self { + case .null: return "null" + case .undefined: return "undefined" + } + } + + /// JS expression that is true when the value is present. `value` is the variable name. + public func presenceCheck(value: String) -> String { + switch self { + case .null: return "\(value) != null" + case .undefined: return "\(value) !== undefined" + } + } +} + public enum BridgeType: Codable, Equatable, Hashable, Sendable { case int, uint, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void case unsafePointer(UnsafePointerType) - indirect case optional(BridgeType) - indirect case undefinedOr(BridgeType) + indirect case nullable(BridgeType, JSOptionalKind) indirect case array(BridgeType) case caseEnum(String) case rawValueEnum(String, SwiftEnumRawType) @@ -896,7 +917,7 @@ extension BridgeType { return .pointer case .unsafePointer: return .pointer - case .optional(_), .undefinedOr(_): + case .nullable: return nil case .caseEnum: return .i32 @@ -921,10 +942,9 @@ extension BridgeType { } } - /// Returns true if this type is optional + /// Returns true if this type is optional (nullable with null or undefined). public var isOptional: Bool { - if case .optional = self { return true } - if case .undefinedOr = self { return true } + if case .nullable = self { return true } return false } @@ -961,10 +981,9 @@ extension BridgeType { return "\(kindCode)\(p.count)\(p)" } return kindCode - case .optional(let wrapped): - return "Sq\(wrapped.mangleTypeName)" - case .undefinedOr(let wrapped): - return "Su\(wrapped.mangleTypeName)" + case .nullable(let wrapped, let kind): + let prefix = kind == .null ? "Sq" : "Su" + return "\(prefix)\(wrapped.mangleTypeName)" case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name), @@ -991,13 +1010,7 @@ extension BridgeType { /// Side channels are needed when the wrapped type cannot be directly returned via WASM, /// or when we need to distinguish null from absent value for certain primitives. public func usesSideChannelForOptionalReturn() -> Bool { - let wrappedType: BridgeType - switch self { - case .optional(let wrapped): - wrappedType = wrapped - case .undefinedOr(let wrapped): - wrappedType = wrapped - default: + guard case .nullable(let wrappedType, _) = self else { return false } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json index 7950cb66..8dde2cfb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json @@ -516,12 +516,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -531,12 +532,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -557,12 +559,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -572,12 +575,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -596,7 +600,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -605,13 +609,14 @@ } } } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -620,7 +625,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -639,12 +645,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -654,12 +661,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Point" } - } + }, + "_1" : "null" } } } @@ -680,12 +688,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } @@ -695,12 +704,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } @@ -721,13 +731,14 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Status", "_1" : "Int" } - } + }, + "_1" : "null" } } } @@ -737,13 +748,14 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Status", "_1" : "Int" } - } + }, + "_1" : "null" } } } @@ -994,12 +1006,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } @@ -1009,12 +1022,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json index 7f21c86c..59fb8484 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.json @@ -12,12 +12,13 @@ "isStatic" : false, "name" : "linkedB", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "ClassB" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json index 3f812910..cc10331a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.json @@ -33,12 +33,13 @@ "isStatic" : false, "name" : "linkedB", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "ClassB" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json index 64cc1a84..8c088a35 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DefaultParameters.json @@ -135,12 +135,13 @@ "label" : "tag", "name" : "tag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -196,12 +197,13 @@ "isStatic" : false, "name" : "tag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -414,23 +416,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -452,23 +456,25 @@ "label" : "greeting", "name" : "greeting", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -646,23 +652,25 @@ "label" : "point", "name" : "point", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } }, @@ -710,23 +718,25 @@ "label" : "point", "name" : "point", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json index ecf045c3..ba9ee932 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumAssociatedValue.json @@ -398,12 +398,13 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -414,23 +415,25 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -441,34 +444,37 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -570,23 +576,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -670,23 +678,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "ComplexResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "ComplexResult" } - } + }, + "_1" : "null" } } }, @@ -703,23 +713,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "Utilities.Result" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "Utilities.Result" } - } + }, + "_1" : "null" } } }, @@ -736,23 +748,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "NetworkingResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "NetworkingResult" } - } + }, + "_1" : "null" } } }, @@ -769,23 +783,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } }, @@ -802,12 +818,13 @@ "label" : "result1", "name" : "result1", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } }, @@ -815,23 +832,25 @@ "label" : "result2", "name" : "result2", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json index 2d5d5f2f..ea32ad73 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumCase.json @@ -216,23 +216,25 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -291,23 +293,25 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "TSDirection" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "TSDirection" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json index 20ef1f42..ba36405b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json @@ -517,25 +517,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -596,25 +598,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSTheme", "_1" : "String" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSTheme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -675,25 +679,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "FeatureFlag", "_1" : "String" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "FeatureFlag", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -754,25 +760,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -833,25 +841,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSHttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSHttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -912,25 +922,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Priority", "_1" : "Int32" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Priority", "_1" : "Int32" } - } + }, + "_1" : "null" } } }, @@ -991,25 +1003,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "FileSize", "_1" : "Int64" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "FileSize", "_1" : "Int64" } - } + }, + "_1" : "null" } } }, @@ -1070,25 +1084,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "UserId", "_1" : "UInt" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "UserId", "_1" : "UInt" } - } + }, + "_1" : "null" } } }, @@ -1149,25 +1165,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TokenId", "_1" : "UInt32" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TokenId", "_1" : "UInt32" } - } + }, + "_1" : "null" } } }, @@ -1228,25 +1246,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "SessionId", "_1" : "UInt64" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "SessionId", "_1" : "UInt64" } - } + }, + "_1" : "null" } } }, @@ -1307,25 +1327,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Precision", "_1" : "Float" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Precision", "_1" : "Float" } - } + }, + "_1" : "null" } } }, @@ -1386,25 +1408,27 @@ "label" : "_", "name" : "input", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Ratio", "_1" : "Double" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Ratio", "_1" : "Double" } - } + }, + "_1" : "null" } } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json index c40f0dc8..cecf16c9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json @@ -73,12 +73,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } @@ -88,12 +89,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } @@ -150,12 +152,13 @@ "isStatic" : false, "name" : "optionalFoo", "type" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json index 2af32886..ea5c675f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json @@ -14,12 +14,13 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -56,12 +57,13 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -80,12 +82,13 @@ "isStatic" : false, "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -114,12 +117,13 @@ "isStatic" : false, "name" : "optionalName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -128,12 +132,13 @@ "isStatic" : false, "name" : "optionalAge", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -142,12 +147,13 @@ "isStatic" : false, "name" : "optionalGreeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -173,23 +179,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -206,23 +214,25 @@ "label" : "_", "name" : "holder", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "OptionalPropertyHolder" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "OptionalPropertyHolder" } - } + }, + "_1" : "null" } } }, @@ -239,23 +249,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -272,23 +284,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -305,23 +319,25 @@ "label" : "flag", "name" : "flag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, @@ -338,23 +354,25 @@ "label" : "number", "name" : "number", "type" : { - "optional" : { + "nullable" : { "_0" : { "float" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "float" : { } - } + }, + "_1" : "null" } } }, @@ -371,23 +389,25 @@ "label" : "precision", "name" : "precision", "type" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -404,23 +424,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -437,23 +459,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -470,23 +494,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -503,23 +529,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -536,23 +564,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -569,23 +599,25 @@ "label" : "age", "name" : "age", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -602,23 +634,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -635,12 +669,13 @@ "label" : "firstName", "name" : "firstName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -648,12 +683,13 @@ "label" : "lastName", "name" : "lastName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -661,12 +697,13 @@ "label" : "age", "name" : "age", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -681,12 +718,13 @@ } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json index 5713a289..e9f5264c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json @@ -237,12 +237,13 @@ "isStatic" : false, "name" : "secondDelegate", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftProtocol" : { "_0" : "MyViewControllerDelegate" } - } + }, + "_1" : "null" } } } @@ -667,12 +668,13 @@ "label" : "_", "name" : "helper", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Helper" } - } + }, + "_1" : "null" } } } @@ -695,12 +697,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Helper" } - } + }, + "_1" : "null" } } }, @@ -789,12 +792,13 @@ "isReadonly" : false, "name" : "optionalName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -802,13 +806,14 @@ "isReadonly" : false, "name" : "optionalRawEnum", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "ExampleEnum", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -835,12 +840,13 @@ "isReadonly" : false, "name" : "optionalResult", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "Result" } - } + }, + "_1" : "null" } } }, @@ -857,12 +863,13 @@ "isReadonly" : false, "name" : "directionOptional", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -880,13 +887,14 @@ "isReadonly" : false, "name" : "priorityOptional", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Priority", "_1" : "Int" } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json index 9b1d0d6b..bb9e4809 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.Global.json @@ -118,12 +118,13 @@ } }, "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json index b3ccf7e9..d385e388 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StaticProperties.json @@ -118,12 +118,13 @@ } }, "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json index 345582e8..d7172682 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.json @@ -202,30 +202,33 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Person" } - } + }, + "_1" : "null" } }, { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } }, { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } ], @@ -325,12 +328,13 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Person" } - } + }, + "_1" : "null" } } ], @@ -353,12 +357,13 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Person" } - } + }, + "_1" : "null" } } ], @@ -700,12 +705,13 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } ], @@ -746,13 +752,14 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } ], @@ -793,12 +800,13 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } ], @@ -838,12 +846,13 @@ "moduleName" : "TestModule", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } ], diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json index 1b2dc531..00c6af5c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStruct.json @@ -184,12 +184,13 @@ "label" : "optCount", "name" : "optCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -197,12 +198,13 @@ "label" : "optFlag", "name" : "optFlag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -248,12 +250,13 @@ "isStatic" : false, "name" : "optCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -262,12 +265,13 @@ "isStatic" : false, "name" : "optFlag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -305,12 +309,13 @@ "isStatic" : false, "name" : "zipCode", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -358,12 +363,13 @@ "isStatic" : false, "name" : "email", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -431,13 +437,14 @@ "isStatic" : false, "name" : "optionalPrecision", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Precision", "_1" : "Float" } - } + }, + "_1" : "null" } } } @@ -563,12 +570,13 @@ "isStatic" : false, "name" : "optionalObject", "type" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index ac60ca86..e337124e 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -105,6 +105,14 @@ public protocol _BridgedSwiftStackType { consuming func bridgeJSLowerStackReturn() } +/// Types that bridge with the same (isSome, value) ABI as Optional. +/// Used by JSUndefinedOr so all bridge methods delegate to Optional. +public protocol _BridgedAsOptional { + associatedtype Wrapped + var optionalRepresentation: Wrapped? { get } + init(optional: Wrapped?) +} + extension Bool: _BridgedSwiftTypeLoweredIntoSingleWasmCoreType, _BridgedSwiftStackType { // MARK: ImportTS @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { @@ -1773,6 +1781,379 @@ extension Optional where Wrapped: _BridgedSwiftStruct { } } +// MARK: - _BridgedAsOptional (JSUndefinedOr) delegating to Optional + +extension _BridgedAsOptional where Wrapped == Bool { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ value: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(value)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == Int { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == UInt { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == String { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ bytes: Int32, + _ count: Int32 + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == JSObject { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftProtocolWrapper { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftHeapObject { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, pointer: UnsafeMutableRawPointer) + { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerParameterWithRetain() -> ( + isSome: Int32, pointer: UnsafeMutableRawPointer + ) { + optionalRepresentation.bridgeJSLowerParameterWithRetain() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ pointer: UnsafeMutableRawPointer + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, pointer)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == Float { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped == Double { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(caseId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == String { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter( + _ isSome: Int32, + _ bytes: Int32, + _ count: Int32 + ) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Int { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Bool { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Float { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional +where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Double { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { + Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, caseId: Int32) { + optionalRepresentation.bridgeJSLowerParameter() + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftReturn(caseId)) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + +extension _BridgedAsOptional where Wrapped: _BridgedSwiftStruct { + @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32) -> Self { + Self(optional: Optional.bridgeJSLiftParameter(isSome)) + } + + @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { + Self(optional: Optional.bridgeJSLiftParameter()) + } + + @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { + optionalRepresentation.bridgeJSLowerReturn() + } +} + // MARK: - Array Support extension Array where Element: _BridgedSwiftStackType, Element.StackLiftResult == Element { diff --git a/Sources/JavaScriptKit/JSUndefinedOr.swift b/Sources/JavaScriptKit/JSUndefinedOr.swift index f70cfc0d..82e21810 100644 --- a/Sources/JavaScriptKit/JSUndefinedOr.swift +++ b/Sources/JavaScriptKit/JSUndefinedOr.swift @@ -6,12 +6,12 @@ public static var undefinedValue: Self { .undefined } @inlinable - init(optional: Wrapped?) { + public init(optional: Wrapped?) { self = optional.map(Self.value) ?? .undefined } @inlinable - var optionalRepresentation: Wrapped? { + public var optionalRepresentation: Wrapped? { switch self { case .undefined: return nil @@ -40,369 +40,6 @@ extension JSUndefinedOr: ConvertibleToJSValue where Wrapped: ConvertibleToJSValu } } -// MARK: - BridgeJS Optional-style conformances +// MARK: - BridgeJS (via _BridgedAsOptional in BridgeJSIntrinsics) -extension JSUndefinedOr where Wrapped == Bool { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ value: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftReturn(value)) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped == Int { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped == UInt { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped == String { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter( - _ isSome: Int32, - _ bytes: Int32, - _ count: Int32 - ) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped == JSObject { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftProtocolWrapper { - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ objectId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, objectId)) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftHeapObject { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, pointer: UnsafeMutableRawPointer) - { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerParameterWithRetain() -> ( - isSome: Int32, pointer: UnsafeMutableRawPointer - ) { - optionalRepresentation.bridgeJSLowerParameterWithRetain() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter( - _ isSome: Int32, - _ pointer: UnsafeMutableRawPointer - ) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, pointer)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } -} - -extension JSUndefinedOr where Wrapped == Float { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped == Double { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftCaseEnum { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftReturn(caseId)) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftTypeLoweredIntoVoidType { - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr -where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == String { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter( - _ isSome: Int32, - _ bytes: Int32, - _ count: Int32 - ) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, bytes, count)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Int { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Bool { - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr -where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Float { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr -where Wrapped: _BridgedSwiftEnumNoPayload, Wrapped: RawRepresentable, Wrapped.RawValue == Double { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, value: Float64) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ wrappedValue: Float64) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, wrappedValue)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturnFromSideChannel() -> Self { - Self(optional: Optional.bridgeJSLiftReturnFromSideChannel()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftAssociatedValueEnum { - @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (isSome: Int32, caseId: Int32) { - optionalRepresentation.bridgeJSLowerParameter() - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32, _ caseId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome, caseId)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public static func bridgeJSLiftReturn(_ caseId: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftReturn(caseId)) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} - -extension JSUndefinedOr where Wrapped: _BridgedSwiftStruct { - @_spi(BridgeJS) public static func bridgeJSLiftParameter(_ isSome: Int32) -> Self { - Self(optional: Optional.bridgeJSLiftParameter(isSome)) - } - - @_spi(BridgeJS) public static func bridgeJSLiftParameter() -> Self { - Self(optional: Optional.bridgeJSLiftParameter()) - } - - @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { - optionalRepresentation.bridgeJSLowerReturn() - } -} +extension JSUndefinedOr: _BridgedAsOptional {} diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index a572f4c6..72b10468 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -4488,9 +4488,9 @@ public func _bjs_applyOptionalGreeter(_ valueIsSome: Int32, _ valueValue: Unsafe @_expose(wasm, "bjs_makeOptionalHolder") @_cdecl("bjs_makeOptionalHolder") -public func _bjs_makeOptionalHolder(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsDefined: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { +public func _bjs_makeOptionalHolder(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { #if arch(wasm32) - let ret = makeOptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsDefined, undefinedNumberValue)) + let ret = makeOptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5909,9 +5909,9 @@ fileprivate func _bjs_TestServer_wrap(_ pointer: UnsafeMutableRawPointer) -> Int @_expose(wasm, "bjs_OptionalHolder_init") @_cdecl("bjs_OptionalHolder_init") -public func _bjs_OptionalHolder_init(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsDefined: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { +public func _bjs_OptionalHolder_init(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { #if arch(wasm32) - let ret = OptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsDefined, undefinedNumberValue)) + let ret = OptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5952,9 +5952,9 @@ public func _bjs_OptionalHolder_undefinedNumber_get(_ _self: UnsafeMutableRawPoi @_expose(wasm, "bjs_OptionalHolder_undefinedNumber_set") @_cdecl("bjs_OptionalHolder_undefinedNumber_set") -public func _bjs_OptionalHolder_undefinedNumber_set(_ _self: UnsafeMutableRawPointer, _ valueIsDefined: Int32, _ valueValue: Float64) -> Void { +public func _bjs_OptionalHolder_undefinedNumber_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: Float64) -> Void { #if arch(wasm32) - OptionalHolder.bridgeJSLiftParameter(_self).undefinedNumber = JSUndefinedOr.bridgeJSLiftParameter(valueIsDefined, valueValue) + OptionalHolder.bridgeJSLiftParameter(_self).undefinedNumber = JSUndefinedOr.bridgeJSLiftParameter(valueIsSome, valueValue) #else fatalError("Only available on WebAssembly") #endif @@ -8034,16 +8034,16 @@ func _$jsRoundTripOptionalNumberNull(_ v: Optional) throws(JSException) #if arch(wasm32) @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalNumberUndefined") -fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsDefined: Int32, _ vValue: Float64) -> Void +fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsSome: Int32, _ vValue: Float64) -> Void #else -fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsDefined: Int32, _ vValue: Float64) -> Void { +fileprivate func bjs_jsRoundTripOptionalNumberUndefined(_ vIsSome: Int32, _ vValue: Float64) -> Void { fatalError("Only available on WebAssembly") } #endif func _$jsRoundTripOptionalNumberUndefined(_ v: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { - let (vIsDefined, vValue) = v.bridgeJSLowerParameter() - bjs_jsRoundTripOptionalNumberUndefined(vIsDefined, vValue) + let (vIsSome, vValue) = v.bridgeJSLowerParameter() + bjs_jsRoundTripOptionalNumberUndefined(vIsSome, vValue) if let error = _swift_js_take_exception() { throw error } diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 3c71881a..f73b25c3 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -538,12 +538,13 @@ "label" : "nullableGreeter", "name" : "nullableGreeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -551,12 +552,13 @@ "label" : "undefinedNumber", "name" : "undefinedNumber", "type" : { - "undefinedOr" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "undefined" } } } @@ -572,12 +574,13 @@ "isStatic" : false, "name" : "nullableGreeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -586,12 +589,13 @@ "isStatic" : false, "name" : "undefinedNumber", "type" : { - "undefinedOr" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "undefined" } } } @@ -611,12 +615,13 @@ "label" : "optionalName", "name" : "optionalName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -632,12 +637,13 @@ "isStatic" : false, "name" : "optionalName", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -646,12 +652,13 @@ "isStatic" : false, "name" : "optionalAge", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -660,12 +667,13 @@ "isStatic" : false, "name" : "optionalGreeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -1130,12 +1138,13 @@ "label" : "tag", "name" : "tag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -1207,12 +1216,13 @@ "isStatic" : false, "name" : "tag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -1366,12 +1376,13 @@ } }, "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -1385,12 +1396,13 @@ } }, "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -1572,12 +1584,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -1610,12 +1623,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -1632,12 +1646,13 @@ "label" : "_", "name" : "tag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -1660,12 +1675,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -1682,12 +1698,13 @@ "label" : "_", "name" : "count", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -1710,12 +1727,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -1732,12 +1750,13 @@ "label" : "_", "name" : "direction", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } @@ -1760,13 +1779,14 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -1783,13 +1803,14 @@ "label" : "_", "name" : "theme", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } } @@ -1812,13 +1833,14 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -1835,13 +1857,14 @@ "label" : "_", "name" : "status", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } } @@ -1864,12 +1887,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -1886,12 +1910,13 @@ "label" : "_", "name" : "apiResult", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -1920,12 +1945,13 @@ "isStatic" : false, "name" : "backupProcessor", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftProtocol" : { "_0" : "DataProcessor" } - } + }, + "_1" : "null" } } } @@ -2110,12 +2136,13 @@ "label" : "_", "name" : "greeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -2138,12 +2165,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -2160,12 +2188,13 @@ "label" : "_", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -2188,12 +2217,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -2225,12 +2255,13 @@ "isStatic" : false, "name" : "optionalTag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -2239,12 +2270,13 @@ "isStatic" : false, "name" : "optionalCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -2253,12 +2285,13 @@ "isStatic" : false, "name" : "direction", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -2267,13 +2300,14 @@ "isStatic" : false, "name" : "optionalTheme", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -2282,13 +2316,14 @@ "isStatic" : false, "name" : "httpStatus", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -2297,12 +2332,13 @@ "isStatic" : false, "name" : "apiResult", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -2321,12 +2357,13 @@ "isStatic" : false, "name" : "optionalHelper", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -2512,12 +2549,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } ], @@ -2558,12 +2596,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } ], @@ -2604,12 +2643,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } ], @@ -2649,12 +2689,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } ], @@ -2689,12 +2730,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -3030,12 +3072,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } ], @@ -3076,13 +3119,14 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } ], @@ -3123,12 +3167,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } ], @@ -3168,12 +3213,13 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } ], @@ -3215,12 +3261,13 @@ "label" : "config", "name" : "config", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } } @@ -3246,12 +3293,13 @@ "isStatic" : false, "name" : "config", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Config" } - } + }, + "_1" : "null" } } } @@ -4315,12 +4363,13 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -4331,23 +4380,25 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -4358,34 +4409,37 @@ "associatedValues" : [ { "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, { "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -6855,23 +6909,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -6888,23 +6944,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -6921,23 +6979,25 @@ "label" : "flag", "name" : "flag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } }, @@ -6954,23 +7014,25 @@ "label" : "number", "name" : "number", "type" : { - "optional" : { + "nullable" : { "_0" : { "float" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "float" : { } - } + }, + "_1" : "null" } } }, @@ -6987,23 +7049,25 @@ "label" : "precision", "name" : "precision", "type" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -7020,23 +7084,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -7053,23 +7119,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -7086,23 +7154,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -7119,23 +7189,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -7152,23 +7224,25 @@ "label" : "age", "name" : "age", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -7185,23 +7259,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Status" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Status" } - } + }, + "_1" : "null" } } }, @@ -7218,25 +7294,27 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -7253,25 +7331,27 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -7288,23 +7368,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "TSDirection" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "TSDirection" } - } + }, + "_1" : "null" } } }, @@ -7321,25 +7403,27 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSTheme", "_1" : "String" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "TSTheme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -7356,23 +7440,25 @@ "label" : "_", "name" : "method", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Networking.API.Method" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Networking.API.Method" } - } + }, + "_1" : "null" } } }, @@ -7389,23 +7475,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -7422,12 +7510,13 @@ "label" : "_", "name" : "r1", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -7435,12 +7524,13 @@ "label" : "_", "name" : "r2", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -7464,23 +7554,25 @@ "label" : "_", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "ComplexResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "ComplexResult" } - } + }, + "_1" : "null" } } }, @@ -7497,23 +7589,25 @@ "label" : "value", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -7530,23 +7624,25 @@ "label" : "_", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -7563,12 +7659,13 @@ "label" : "_", "name" : "value", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -7584,22 +7681,24 @@ "moduleName" : "BridgeJSRuntimeTests", "parameters" : [ { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -7608,12 +7707,13 @@ } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -7630,12 +7730,13 @@ "label" : "nullableGreeter", "name" : "nullableGreeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -7643,12 +7744,13 @@ "label" : "undefinedNumber", "name" : "undefinedNumber", "type" : { - "undefinedOr" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "undefined" } } } @@ -7672,23 +7774,25 @@ "label" : "result", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIOptionalResult" } - } + }, + "_1" : "null" } } }, @@ -7929,23 +8033,25 @@ "label" : "name", "name" : "name", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -8237,7 +8343,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -8246,7 +8352,8 @@ } } } - } + }, + "_1" : "null" } } } @@ -8814,12 +8921,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -8829,12 +8937,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -8855,12 +8964,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -8870,12 +8980,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -8896,12 +9007,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "DataPoint" } - } + }, + "_1" : "null" } } } @@ -8911,12 +9023,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "DataPoint" } - } + }, + "_1" : "null" } } } @@ -8937,12 +9050,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } @@ -8952,12 +9066,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } } @@ -8978,12 +9093,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Status" } - } + }, + "_1" : "null" } } } @@ -8993,12 +9109,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Status" } - } + }, + "_1" : "null" } } } @@ -9017,7 +9134,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9026,13 +9143,14 @@ } } } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9041,7 +9159,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -9058,7 +9177,7 @@ "label" : "_", "name" : "values", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9067,13 +9186,14 @@ } } } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9082,7 +9202,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -9099,7 +9220,7 @@ "label" : "_", "name" : "greeters", "type" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9108,13 +9229,14 @@ } } } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "array" : { "_0" : { @@ -9123,7 +9245,8 @@ } } } - } + }, + "_1" : "null" } } }, @@ -9713,12 +9836,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } @@ -9728,12 +9852,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } @@ -9787,12 +9912,13 @@ "type" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } @@ -9802,12 +9928,13 @@ "returnType" : { "array" : { "_0" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } @@ -10148,12 +10275,13 @@ "label" : "_", "name" : "newResult", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -10417,12 +10545,13 @@ "label" : "_", "name" : "greeter", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -10445,12 +10574,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } }, @@ -10467,12 +10597,13 @@ "label" : "_", "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -10495,12 +10626,13 @@ ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -10529,12 +10661,13 @@ "isReadonly" : false, "name" : "optionalTag", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -10542,12 +10675,13 @@ "isReadonly" : false, "name" : "optionalCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -10555,12 +10689,13 @@ "isReadonly" : false, "name" : "direction", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -10568,13 +10703,14 @@ "isReadonly" : false, "name" : "optionalTheme", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -10582,13 +10718,14 @@ "isReadonly" : false, "name" : "httpStatus", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "HttpStatus", "_1" : "Int" } - } + }, + "_1" : "null" } } }, @@ -10596,12 +10733,13 @@ "isReadonly" : false, "name" : "apiResult", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -10618,12 +10756,13 @@ "isReadonly" : false, "name" : "optionalHelper", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -10838,12 +10977,13 @@ "label" : "optCount", "name" : "optCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -10851,12 +10991,13 @@ "label" : "optFlag", "name" : "optFlag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -10902,12 +11043,13 @@ "isStatic" : false, "name" : "optCount", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } }, @@ -10916,12 +11058,13 @@ "isStatic" : false, "name" : "optFlag", "type" : { - "optional" : { + "nullable" : { "_0" : { "bool" : { } - } + }, + "_1" : "null" } } } @@ -10959,12 +11102,13 @@ "isStatic" : false, "name" : "zipCode", "type" : { - "optional" : { + "nullable" : { "_0" : { "int" : { } - } + }, + "_1" : "null" } } } @@ -11012,12 +11156,13 @@ "isStatic" : false, "name" : "email", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } }, @@ -11026,12 +11171,13 @@ "isStatic" : false, "name" : "secondaryAddress", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Address" } - } + }, + "_1" : "null" } } } @@ -11059,13 +11205,14 @@ "isStatic" : false, "name" : "theme", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Theme", "_1" : "String" } - } + }, + "_1" : "null" } } }, @@ -11074,12 +11221,13 @@ "isStatic" : false, "name" : "direction", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Direction" } - } + }, + "_1" : "null" } } }, @@ -11117,12 +11265,13 @@ "isStatic" : false, "name" : "owner", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftHeapObject" : { "_0" : "Greeter" } - } + }, + "_1" : "null" } } } @@ -11160,12 +11309,13 @@ "isStatic" : false, "name" : "status", "type" : { - "optional" : { + "nullable" : { "_0" : { "caseEnum" : { "_0" : "Status" } - } + }, + "_1" : "null" } } }, @@ -11174,12 +11324,13 @@ "isStatic" : false, "name" : "outcome", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } } @@ -11248,12 +11399,13 @@ "isStatic" : false, "name" : "result", "type" : { - "optional" : { + "nullable" : { "_0" : { "associatedValueEnum" : { "_0" : "APIResult" } - } + }, + "_1" : "null" } } }, @@ -11262,12 +11414,13 @@ "isStatic" : false, "name" : "metadata", "type" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } }, @@ -11276,12 +11429,13 @@ "isStatic" : false, "name" : "location", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "DataPoint" } - } + }, + "_1" : "null" } } }, @@ -11300,12 +11454,13 @@ "isStatic" : false, "name" : "overrideDefaults", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "ConfigStruct" } - } + }, + "_1" : "null" } } } @@ -11345,13 +11500,14 @@ "isStatic" : false, "name" : "optionalPrecision", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Precision", "_1" : "Float" } - } + }, + "_1" : "null" } } }, @@ -11360,13 +11516,14 @@ "isStatic" : false, "name" : "optionalRatio", "type" : { - "optional" : { + "nullable" : { "_0" : { "rawValueEnum" : { "_0" : "Ratio", "_1" : "Double" } - } + }, + "_1" : "null" } } } @@ -11577,12 +11734,13 @@ "isStatic" : false, "name" : "note", "type" : { - "optional" : { + "nullable" : { "_0" : { "string" : { } - } + }, + "_1" : "null" } } } @@ -11678,12 +11836,13 @@ "isStatic" : false, "name" : "shippingAddress", "type" : { - "optional" : { + "nullable" : { "_0" : { "swiftStruct" : { "_0" : "Address" } - } + }, + "_1" : "null" } } } @@ -11800,12 +11959,13 @@ "isStatic" : false, "name" : "optionalObject", "type" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { } - } + }, + "_1" : "null" } } } @@ -11833,12 +11993,13 @@ "isStatic" : false, "name" : "optionalFoo", "type" : { - "optional" : { + "nullable" : { "_0" : { "jsObject" : { "_0" : "Foo" } - } + }, + "_1" : "null" } } } @@ -11960,23 +12121,25 @@ { "name" : "v", "type" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } } ], "returnType" : { - "optional" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "null" } } }, @@ -11986,23 +12149,25 @@ { "name" : "v", "type" : { - "undefinedOr" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "undefined" } } } ], "returnType" : { - "undefinedOr" : { + "nullable" : { "_0" : { "double" : { } - } + }, + "_1" : "undefined" } } }, From d6551ac8aff85147de52d559c6ee0dff2ce048f0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 14:02:40 +0900 Subject: [PATCH 3/6] BridgeJS: Add te2swift unit tests --- .../TS2Swift/JavaScript/src/processor.js | 2 +- .../test/__snapshots__/ts2swift.test.js.snap | 41 +++++++++++++++++++ .../test/fixtures/OptionalNullUndefined.d.ts | 20 +++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/OptionalNullUndefined.d.ts diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js index fd31967b..0e458ed4 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js @@ -691,7 +691,7 @@ export class TypeProcessor { if (nonNullableTypes.length === 1 && (hasNull || hasUndefined)) { const wrapped = this.visitType(nonNullableTypes[0], node); if (hasNull && hasUndefined) { - return `JSUndefinedOr>`; + return "JSObject"; } if (hasNull) { return `Optional<${wrapped}>`; diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap index 6add64de..c1db3e32 100644 --- a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap @@ -145,6 +145,47 @@ exports[`ts2swift > snapshots Swift output for MultipleImportedTypes.d.ts > Mult " `; +exports[`ts2swift > snapshots Swift output for OptionalNullUndefined.d.ts > OptionalNullUndefined 1`] = ` +"// 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(Experimental) @_spi(BridgeJS) import JavaScriptKit + +@JSFunction func roundTripNumberNull(_ value: Optional) throws(JSException) -> Optional + +@JSFunction func roundTripNumberUndefined(_ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + +@JSFunction func roundTripStringNull(_ value: Optional) throws(JSException) -> Optional + +@JSFunction func roundTripStringUndefined(_ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + +@JSFunction func roundTripBooleanNull(_ value: JSObject) throws(JSException) -> JSObject + +@JSFunction func roundTripBooleanUndefined(_ value: JSObject) throws(JSException) -> JSObject + +@JSFunction func optionalNumberParamNull(_ x: Double, _ maybe: Optional) throws(JSException) -> Double + +@JSFunction func optionalNumberParamUndefined(_ x: Double, _ maybe: JSUndefinedOr) throws(JSException) -> Double + +@JSFunction func roundTripMyInterfaceNull(_ value: Optional) throws(JSException) -> Optional + +@JSClass struct MyInterface { +} + +@JSFunction func roundTripMyInterfaceUndefined(_ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + +@JSClass struct WithOptionalFields { + @JSGetter var valueOrNull: Optional + @JSSetter func setValueOrNull(_ value: Optional) throws(JSException) + @JSGetter var valueOrUndefined: JSUndefinedOr + @JSSetter func setValueOrUndefined(_ value: JSUndefinedOr) throws(JSException) +} +" +`; + exports[`ts2swift > snapshots Swift output for PrimitiveParameters.d.ts > PrimitiveParameters 1`] = ` "// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, // DO NOT EDIT. diff --git a/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/OptionalNullUndefined.d.ts b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/OptionalNullUndefined.d.ts new file mode 100644 index 00000000..8b64528d --- /dev/null +++ b/Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/fixtures/OptionalNullUndefined.d.ts @@ -0,0 +1,20 @@ +export function roundTripNumberNull(value: number | null): number | null; +export function roundTripNumberUndefined(value: number | undefined): number | undefined; + +export function roundTripStringNull(value: string | null): string | null; +export function roundTripStringUndefined(value: string | undefined): string | undefined; + +export function roundTripBooleanNull(value: boolean | null): boolean | null; +export function roundTripBooleanUndefined(value: boolean | undefined): boolean | undefined; + +export function optionalNumberParamNull(x: number, maybe: number | null): number; +export function optionalNumberParamUndefined(x: number, maybe: number | undefined): number; + +export interface MyInterface {} +export function roundTripMyInterfaceNull(value: MyInterface | null): MyInterface | null; +export function roundTripMyInterfaceUndefined(value: MyInterface | undefined): MyInterface | undefined; + +export class WithOptionalFields { + valueOrNull: MyInterface | null; + valueOrUndefined: MyInterface | undefined; +} \ No newline at end of file From ab2c0682d4d188cc89a25f426e150d07411a6137 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 14:10:37 +0900 Subject: [PATCH 4/6] BridgeJS: Simplify optional presence check handling --- .../Sources/BridgeJSLink/JSGlueGen.swift | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 44e751d3..90ea5ab0 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -643,10 +643,7 @@ struct IntrinsicJSFragment: Sendable { ) } - static func optionalLowerReturn( - wrappedType: BridgeType, - presenceCheck: (@Sendable (String) -> String)? = nil - ) throws -> IntrinsicJSFragment { + static func optionalLowerReturn(wrappedType: BridgeType, kind: JSOptionalKind) throws -> IntrinsicJSFragment { switch wrappedType { case .void, .nullable, .namespaceEnum, .closure: throw BridgeJSLinkError(message: "Unsupported optional wrapped type for protocol export: \(wrappedType)") @@ -658,7 +655,7 @@ struct IntrinsicJSFragment: Sendable { printCode: { arguments, scope, printer, cleanupCode in let value = arguments[0] let isSomeVar = scope.variable("isSome") - let presenceExpr = presenceCheck?(value) ?? "\(value) != null" + let presenceExpr = kind.presenceCheck(value: value) printer.write("const \(isSomeVar) = \(presenceExpr);") switch wrappedType { @@ -1599,10 +1596,7 @@ struct IntrinsicJSFragment: Sendable { case .swiftProtocol: return .jsObjectLowerReturn case .void: return .void case .nullable(let wrappedType, let kind): - return try .optionalLowerReturn( - wrappedType: wrappedType, - presenceCheck: { value in kind.presenceCheck(value: value) } - ) + return try .optionalLowerReturn(wrappedType: wrappedType, kind: kind) case .caseEnum: return .identity case .rawValueEnum(_, let rawType): switch rawType { @@ -2493,7 +2487,7 @@ struct IntrinsicJSFragment: Sendable { case .nullable(let wrappedType, let kind): return try optionalElementLowerFragment( wrappedType: wrappedType, - presenceCheck: { kind.presenceCheck(value: $0) } + kind: kind ) case .swiftProtocol: // Same as jsObject but no cleanup — Swift's AnyProtocol wrapper releases via deinit @@ -2556,7 +2550,7 @@ struct IntrinsicJSFragment: Sendable { private static func optionalElementLowerFragment( wrappedType: BridgeType, - presenceCheck: (@Sendable (String) -> String)? = nil + kind: JSOptionalKind ) throws -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["value"], @@ -2564,7 +2558,7 @@ struct IntrinsicJSFragment: Sendable { let value = arguments[0] let isSomeVar = scope.variable("isSome") - let presenceExpr = presenceCheck?(value) ?? "\(value) != null" + let presenceExpr = kind.presenceCheck(value: value) printer.write("const \(isSomeVar) = \(presenceExpr) ? 1 : 0;") // Cleanup is written inside the if block so retained id is in scope let localCleanupWriter = CodeFragmentPrinter() From 667d787dd3371fd887a427337a004b629d7bbe62 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 14:19:38 +0900 Subject: [PATCH 5/6] BridgeJS: Cover Optional/Undefined usage on import side --- .../Inputs/MacroSwift/Optionals.swift | 32 ++ .../BridgeJSCodegenTests/Optionals.json | 490 ++++++++++++++++++ .../BridgeJSCodegenTests/Optionals.swift | 461 +++++++++++++++- .../BridgeJSLinkTests/Optionals.d.ts | 21 + .../BridgeJSLinkTests/Optionals.js | 268 ++++++++++ 5 files changed, 1271 insertions(+), 1 deletion(-) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift index d256a875..3fabe55c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift @@ -90,3 +90,35 @@ typealias OptionalNameAlias = Optional func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active: Bool) -> String? { return nil } + +@JSClass struct WithOptionalJSClass { + @JSFunction init(valueOrNull: String?, valueOrUndefined: JSUndefinedOr) throws(JSException) + + @JSGetter var stringOrNull: String? + @JSSetter func setStringOrNull(_ value: String?) throws(JSException) + @JSGetter var stringOrUndefined: JSUndefinedOr + @JSSetter func setStringOrUndefined(_ value: JSUndefinedOr) throws(JSException) + @JSFunction func roundTripStringOrNull(value: String?) throws(JSException) -> String? + @JSFunction func roundTripStringOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + + @JSGetter var doubleOrNull: Double? + @JSSetter func setDoubleOrNull(_ value: Double?) throws(JSException) + @JSGetter var doubleOrUndefined: JSUndefinedOr + @JSSetter func setDoubleOrUndefined(_ value: JSUndefinedOr) throws(JSException) + @JSFunction func roundTripDoubleOrNull(value: Double?) throws(JSException) -> Double? + @JSFunction func roundTripDoubleOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + + @JSGetter var boolOrNull: Bool? + @JSSetter func setBoolOrNull(_ value: Bool?) throws(JSException) + @JSGetter var boolOrUndefined: JSUndefinedOr + @JSSetter func setBoolOrUndefined(_ value: JSUndefinedOr) throws(JSException) + @JSFunction func roundTripBoolOrNull(value: Bool?) throws(JSException) -> Bool? + @JSFunction func roundTripBoolOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + + @JSGetter var intOrNull: Int? + @JSSetter func setIntOrNull(_ value: Int?) throws(JSException) + @JSGetter var intOrUndefined: JSUndefinedOr + @JSSetter func setIntOrUndefined(_ value: JSUndefinedOr) throws(JSException) + @JSFunction func roundTripIntOrNull(value: Int?) throws(JSException) -> Int? + @JSFunction func roundTripIntOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json index ea5c675f..f74b3cdf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json @@ -736,5 +736,495 @@ ] }, + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "constructor" : { + "parameters" : [ + { + "name" : "valueOrNull", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "valueOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "undefined" + } + } + } + ] + }, + "getters" : [ + { + "name" : "stringOrNull", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "stringOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "doubleOrNull", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "doubleOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "boolOrNull", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "boolOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "intOrNull", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "intOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "methods" : [ + { + "name" : "roundTripStringOrNull", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "roundTripStringOrUndefined", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "roundTripDoubleOrNull", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "roundTripDoubleOrUndefined", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "roundTripBoolOrNull", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "roundTripBoolOrUndefined", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "name" : "roundTripIntOrNull", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "null" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "null" + } + } + }, + { + "name" : "roundTripIntOrUndefined", + "parameters" : [ + { + "name" : "value", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "returnType" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "undefined" + } + } + } + ], + "name" : "WithOptionalJSClass", + "setters" : [ + { + "functionName" : "stringOrNull_set", + "name" : "stringOrNull", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "null" + } + } + }, + { + "functionName" : "stringOrUndefined_set", + "name" : "stringOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "string" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "functionName" : "doubleOrNull_set", + "name" : "doubleOrNull", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "null" + } + } + }, + { + "functionName" : "doubleOrUndefined_set", + "name" : "doubleOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "double" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "functionName" : "boolOrNull_set", + "name" : "boolOrNull", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "null" + } + } + }, + { + "functionName" : "boolOrUndefined_set", + "name" : "boolOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "bool" : { + + } + }, + "_1" : "undefined" + } + } + }, + { + "functionName" : "intOrNull_set", + "name" : "intOrNull", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "null" + } + } + }, + { + "functionName" : "intOrUndefined_set", + "name" : "intOrUndefined", + "type" : { + "nullable" : { + "_0" : { + "int" : { + + } + }, + "_1" : "undefined" + } + } + } + ] + } + ] + } + ] + }, "moduleName" : "TestModule" } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift index 88195f37..471d38a6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift @@ -338,4 +338,463 @@ fileprivate func _bjs_OptionalPropertyHolder_wrap(_ pointer: UnsafeMutableRawPoi fileprivate func _bjs_OptionalPropertyHolder_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 { fatalError("Only available on WebAssembly") } -#endif \ No newline at end of file +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_init") +fileprivate func bjs_WithOptionalJSClass_init(_ valueOrNullIsSome: Int32, _ valueOrNullValue: Int32, _ valueOrUndefinedIsSome: Int32, _ valueOrUndefinedValue: Int32) -> Int32 +#else +fileprivate func bjs_WithOptionalJSClass_init(_ valueOrNullIsSome: Int32, _ valueOrNullValue: Int32, _ valueOrUndefinedIsSome: Int32, _ valueOrUndefinedValue: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrNull_get") +fileprivate func bjs_WithOptionalJSClass_stringOrNull_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_stringOrNull_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrUndefined_get") +fileprivate func bjs_WithOptionalJSClass_stringOrUndefined_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_stringOrUndefined_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_doubleOrNull_get") +fileprivate func bjs_WithOptionalJSClass_doubleOrNull_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_doubleOrNull_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_doubleOrUndefined_get") +fileprivate func bjs_WithOptionalJSClass_doubleOrUndefined_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_doubleOrUndefined_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_boolOrNull_get") +fileprivate func bjs_WithOptionalJSClass_boolOrNull_get(_ self: Int32) -> Int32 +#else +fileprivate func bjs_WithOptionalJSClass_boolOrNull_get(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_boolOrUndefined_get") +fileprivate func bjs_WithOptionalJSClass_boolOrUndefined_get(_ self: Int32) -> Int32 +#else +fileprivate func bjs_WithOptionalJSClass_boolOrUndefined_get(_ self: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_intOrNull_get") +fileprivate func bjs_WithOptionalJSClass_intOrNull_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_intOrNull_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_intOrUndefined_get") +fileprivate func bjs_WithOptionalJSClass_intOrUndefined_get(_ self: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_intOrUndefined_get(_ self: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrNull_set") +fileprivate func bjs_WithOptionalJSClass_stringOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_stringOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrUndefined_set") +fileprivate func bjs_WithOptionalJSClass_stringOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_stringOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_doubleOrNull_set") +fileprivate func bjs_WithOptionalJSClass_doubleOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Float64) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_doubleOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_doubleOrUndefined_set") +fileprivate func bjs_WithOptionalJSClass_doubleOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Float64) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_doubleOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_boolOrNull_set") +fileprivate func bjs_WithOptionalJSClass_boolOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_boolOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_boolOrUndefined_set") +fileprivate func bjs_WithOptionalJSClass_boolOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_boolOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_intOrNull_set") +fileprivate func bjs_WithOptionalJSClass_intOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_intOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_intOrUndefined_set") +fileprivate func bjs_WithOptionalJSClass_intOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_intOrUndefined_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripStringOrNull") +fileprivate func bjs_WithOptionalJSClass_roundTripStringOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripStringOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripStringOrUndefined") +fileprivate func bjs_WithOptionalJSClass_roundTripStringOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripStringOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripDoubleOrNull") +fileprivate func bjs_WithOptionalJSClass_roundTripDoubleOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Float64) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripDoubleOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripDoubleOrUndefined") +fileprivate func bjs_WithOptionalJSClass_roundTripDoubleOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Float64) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripDoubleOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Float64) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripBoolOrNull") +fileprivate func bjs_WithOptionalJSClass_roundTripBoolOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Int32 +#else +fileprivate func bjs_WithOptionalJSClass_roundTripBoolOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripBoolOrUndefined") +fileprivate func bjs_WithOptionalJSClass_roundTripBoolOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Int32 +#else +fileprivate func bjs_WithOptionalJSClass_roundTripBoolOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripIntOrNull") +fileprivate func bjs_WithOptionalJSClass_roundTripIntOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripIntOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripIntOrUndefined") +fileprivate func bjs_WithOptionalJSClass_roundTripIntOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void +#else +fileprivate func bjs_WithOptionalJSClass_roundTripIntOrUndefined(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif + +func _$WithOptionalJSClass_init(_ valueOrNull: Optional, _ valueOrUndefined: JSUndefinedOr) throws(JSException) -> JSObject { + let (valueOrNullIsSome, valueOrNullValue) = valueOrNull.bridgeJSLowerParameter() + let (valueOrUndefinedIsSome, valueOrUndefinedValue) = valueOrUndefined.bridgeJSLowerParameter() + let ret = bjs_WithOptionalJSClass_init(valueOrNullIsSome, valueOrNullValue, valueOrUndefinedIsSome, valueOrUndefinedValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSObject.bridgeJSLiftReturn(ret) +} + +func _$WithOptionalJSClass_stringOrNull_get(_ self: JSObject) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_stringOrNull_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_stringOrUndefined_get(_ self: JSObject) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_stringOrUndefined_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_doubleOrNull_get(_ self: JSObject) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_doubleOrNull_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_doubleOrUndefined_get(_ self: JSObject) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_doubleOrUndefined_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_boolOrNull_get(_ self: JSObject) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WithOptionalJSClass_boolOrNull_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn(ret) +} + +func _$WithOptionalJSClass_boolOrUndefined_get(_ self: JSObject) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + let ret = bjs_WithOptionalJSClass_boolOrUndefined_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturn(ret) +} + +func _$WithOptionalJSClass_intOrNull_get(_ self: JSObject) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_intOrNull_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_intOrUndefined_get(_ self: JSObject) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_intOrUndefined_get(selfValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_stringOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_stringOrNull_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_stringOrUndefined_set(_ self: JSObject, _ newValue: JSUndefinedOr) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_stringOrUndefined_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_doubleOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_doubleOrNull_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_doubleOrUndefined_set(_ self: JSObject, _ newValue: JSUndefinedOr) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_doubleOrUndefined_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_boolOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_boolOrNull_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_boolOrUndefined_set(_ self: JSObject, _ newValue: JSUndefinedOr) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_boolOrUndefined_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_intOrNull_set(_ self: JSObject, _ newValue: Optional) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_intOrNull_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_intOrUndefined_set(_ self: JSObject, _ newValue: JSUndefinedOr) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_intOrUndefined_set(selfValue, newValueIsSome, newValueValue) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$WithOptionalJSClass_roundTripStringOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripStringOrNull(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripStringOrUndefined(_ self: JSObject, _ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripStringOrUndefined(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripDoubleOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripDoubleOrNull(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripDoubleOrUndefined(_ self: JSObject, _ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripDoubleOrUndefined(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripBoolOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + let ret = bjs_WithOptionalJSClass_roundTripBoolOrNull(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturn(ret) +} + +func _$WithOptionalJSClass_roundTripBoolOrUndefined(_ self: JSObject, _ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + let ret = bjs_WithOptionalJSClass_roundTripBoolOrUndefined(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturn(ret) +} + +func _$WithOptionalJSClass_roundTripIntOrNull(_ self: JSObject, _ value: Optional) throws(JSException) -> Optional { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripIntOrNull(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return Optional.bridgeJSLiftReturnFromSideChannel() +} + +func _$WithOptionalJSClass_roundTripIntOrUndefined(_ self: JSObject, _ value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr { + let selfValue = self.bridgeJSLowerParameter() + let (valueIsSome, valueValue) = value.bridgeJSLowerParameter() + bjs_WithOptionalJSClass_roundTripIntOrUndefined(selfValue, valueIsSome, valueValue) + if let error = _swift_js_take_exception() { + throw error + } + return JSUndefinedOr.bridgeJSLiftReturnFromSideChannel() +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts index 5f63e9db..b1a67ccd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts @@ -21,6 +21,24 @@ export interface OptionalPropertyHolder extends SwiftHeapObject { optionalAge: number | null; optionalGreeter: Greeter | null; } +export interface WithOptionalJSClass { + roundTripStringOrNull(value: string | null): string | null; + roundTripStringOrUndefined(value: string | undefined): string | undefined; + roundTripDoubleOrNull(value: number | null): number | null; + roundTripDoubleOrUndefined(value: number | undefined): number | undefined; + roundTripBoolOrNull(value: boolean | null): boolean | null; + roundTripBoolOrUndefined(value: boolean | undefined): boolean | undefined; + roundTripIntOrNull(value: number | null): number | null; + roundTripIntOrUndefined(value: number | undefined): number | undefined; + stringOrNull: string | null; + stringOrUndefined: string | undefined; + doubleOrNull: number | null; + doubleOrUndefined: number | undefined; + boolOrNull: boolean | null; + boolOrUndefined: boolean | undefined; + intOrNull: number | null; + intOrUndefined: number | undefined; +} export type Exports = { Greeter: { new(name: string | null): Greeter; @@ -45,6 +63,9 @@ export type Exports = { testMixedOptionals(firstName: string | null, lastName: string | null, age: number | null, active: boolean): string | null; } export type Imports = { + WithOptionalJSClass: { + new(valueOrNull: string | null, valueOrUndefined: string | undefined): WithOptionalJSClass; + } } export function createInstantiator(options: { imports: Imports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js index 2c08e3d9..0ec2f81c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js @@ -42,6 +42,7 @@ export async function createInstantiator(options, swift) { addImports: (importObject, importsContext) => { bjs = {}; importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); bjs["swift_js_return_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); tmpRetString = textDecoder.decode(bytes); @@ -213,6 +214,273 @@ export async function createInstantiator(options, swift) { const obj = OptionalPropertyHolder.__construct(pointer); return swift.memory.retain(obj); }; + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_WithOptionalJSClass_init"] = function bjs_WithOptionalJSClass_init(valueOrNullIsSome, valueOrNullWrappedValue, valueOrUndefinedIsSome, valueOrUndefinedWrappedValue) { + try { + let obj; + if (valueOrNullIsSome) { + obj = swift.memory.getObject(valueOrNullWrappedValue); + swift.memory.release(valueOrNullWrappedValue); + } + let obj1; + if (valueOrUndefinedIsSome) { + obj1 = swift.memory.getObject(valueOrUndefinedWrappedValue); + swift.memory.release(valueOrUndefinedWrappedValue); + } + return swift.memory.retain(new imports.WithOptionalJSClass(valueOrNullIsSome ? obj : null, valueOrUndefinedIsSome ? obj1 : undefined)); + } catch (error) { + setException(error); + return 0 + } + } + TestModule["bjs_WithOptionalJSClass_stringOrNull_get"] = function bjs_WithOptionalJSClass_stringOrNull_get(self) { + try { + let ret = swift.memory.getObject(self).stringOrNull; + const isSome = ret != null; + if (isSome) { + const bytes = textEncoder.encode(ret); + bjs["swift_js_return_optional_string"](1, bytes, bytes.length); + return bytes.length; + } else { + bjs["swift_js_return_optional_string"](0, 0, 0); + return -1; + } + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_stringOrUndefined_get"] = function bjs_WithOptionalJSClass_stringOrUndefined_get(self) { + try { + let ret = swift.memory.getObject(self).stringOrUndefined; + const isSome = ret !== undefined; + if (isSome) { + const bytes = textEncoder.encode(ret); + bjs["swift_js_return_optional_string"](1, bytes, bytes.length); + return bytes.length; + } else { + bjs["swift_js_return_optional_string"](0, 0, 0); + return -1; + } + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_doubleOrNull_get"] = function bjs_WithOptionalJSClass_doubleOrNull_get(self) { + try { + let ret = swift.memory.getObject(self).doubleOrNull; + const isSome = ret != null; + bjs["swift_js_return_optional_double"](isSome ? 1 : 0, isSome ? ret : 0.0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_doubleOrUndefined_get"] = function bjs_WithOptionalJSClass_doubleOrUndefined_get(self) { + try { + let ret = swift.memory.getObject(self).doubleOrUndefined; + const isSome = ret !== undefined; + bjs["swift_js_return_optional_double"](isSome ? 1 : 0, isSome ? ret : 0.0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_boolOrNull_get"] = function bjs_WithOptionalJSClass_boolOrNull_get(self) { + try { + let ret = swift.memory.getObject(self).boolOrNull; + const isSome = ret != null; + bjs["swift_js_return_optional_bool"](isSome ? 1 : 0, isSome ? (ret ? 1 : 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_boolOrUndefined_get"] = function bjs_WithOptionalJSClass_boolOrUndefined_get(self) { + try { + let ret = swift.memory.getObject(self).boolOrUndefined; + const isSome = ret !== undefined; + bjs["swift_js_return_optional_bool"](isSome ? 1 : 0, isSome ? (ret ? 1 : 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_intOrNull_get"] = function bjs_WithOptionalJSClass_intOrNull_get(self) { + try { + let ret = swift.memory.getObject(self).intOrNull; + const isSome = ret != null; + bjs["swift_js_return_optional_int"](isSome ? 1 : 0, isSome ? (ret | 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_intOrUndefined_get"] = function bjs_WithOptionalJSClass_intOrUndefined_get(self) { + try { + let ret = swift.memory.getObject(self).intOrUndefined; + const isSome = ret !== undefined; + bjs["swift_js_return_optional_int"](isSome ? 1 : 0, isSome ? (ret | 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_stringOrNull_set"] = function bjs_WithOptionalJSClass_stringOrNull_set(self, newValueIsSome, newValueWrappedValue) { + try { + let obj; + if (newValueIsSome) { + obj = swift.memory.getObject(newValueWrappedValue); + swift.memory.release(newValueWrappedValue); + } + swift.memory.getObject(self).stringOrNull = newValueIsSome ? obj : null; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_stringOrUndefined_set"] = function bjs_WithOptionalJSClass_stringOrUndefined_set(self, newValueIsSome, newValueWrappedValue) { + try { + let obj; + if (newValueIsSome) { + obj = swift.memory.getObject(newValueWrappedValue); + swift.memory.release(newValueWrappedValue); + } + swift.memory.getObject(self).stringOrUndefined = newValueIsSome ? obj : undefined; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_doubleOrNull_set"] = function bjs_WithOptionalJSClass_doubleOrNull_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).doubleOrNull = newValueIsSome ? newValueWrappedValue : null; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_doubleOrUndefined_set"] = function bjs_WithOptionalJSClass_doubleOrUndefined_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).doubleOrUndefined = newValueIsSome ? newValueWrappedValue : undefined; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_boolOrNull_set"] = function bjs_WithOptionalJSClass_boolOrNull_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).boolOrNull = newValueIsSome ? newValueWrappedValue !== 0 : null; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_boolOrUndefined_set"] = function bjs_WithOptionalJSClass_boolOrUndefined_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).boolOrUndefined = newValueIsSome ? newValueWrappedValue !== 0 : undefined; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_intOrNull_set"] = function bjs_WithOptionalJSClass_intOrNull_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).intOrNull = newValueIsSome ? newValueWrappedValue : null; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_intOrUndefined_set"] = function bjs_WithOptionalJSClass_intOrUndefined_set(self, newValueIsSome, newValueWrappedValue) { + try { + swift.memory.getObject(self).intOrUndefined = newValueIsSome ? newValueWrappedValue : undefined; + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripStringOrNull"] = function bjs_WithOptionalJSClass_roundTripStringOrNull(self, valueIsSome, valueWrappedValue) { + try { + let obj; + if (valueIsSome) { + obj = swift.memory.getObject(valueWrappedValue); + swift.memory.release(valueWrappedValue); + } + let ret = swift.memory.getObject(self).roundTripStringOrNull(valueIsSome ? obj : null); + const isSome = ret != null; + if (isSome) { + const bytes = textEncoder.encode(ret); + bjs["swift_js_return_optional_string"](1, bytes, bytes.length); + return bytes.length; + } else { + bjs["swift_js_return_optional_string"](0, 0, 0); + return -1; + } + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripStringOrUndefined"] = function bjs_WithOptionalJSClass_roundTripStringOrUndefined(self, valueIsSome, valueWrappedValue) { + try { + let obj; + if (valueIsSome) { + obj = swift.memory.getObject(valueWrappedValue); + swift.memory.release(valueWrappedValue); + } + let ret = swift.memory.getObject(self).roundTripStringOrUndefined(valueIsSome ? obj : undefined); + const isSome = ret !== undefined; + if (isSome) { + const bytes = textEncoder.encode(ret); + bjs["swift_js_return_optional_string"](1, bytes, bytes.length); + return bytes.length; + } else { + bjs["swift_js_return_optional_string"](0, 0, 0); + return -1; + } + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripDoubleOrNull"] = function bjs_WithOptionalJSClass_roundTripDoubleOrNull(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripDoubleOrNull(valueIsSome ? valueWrappedValue : null); + const isSome = ret != null; + bjs["swift_js_return_optional_double"](isSome ? 1 : 0, isSome ? ret : 0.0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripDoubleOrUndefined"] = function bjs_WithOptionalJSClass_roundTripDoubleOrUndefined(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripDoubleOrUndefined(valueIsSome ? valueWrappedValue : undefined); + const isSome = ret !== undefined; + bjs["swift_js_return_optional_double"](isSome ? 1 : 0, isSome ? ret : 0.0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripBoolOrNull"] = function bjs_WithOptionalJSClass_roundTripBoolOrNull(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripBoolOrNull(valueIsSome ? valueWrappedValue !== 0 : null); + const isSome = ret != null; + bjs["swift_js_return_optional_bool"](isSome ? 1 : 0, isSome ? (ret ? 1 : 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripBoolOrUndefined"] = function bjs_WithOptionalJSClass_roundTripBoolOrUndefined(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripBoolOrUndefined(valueIsSome ? valueWrappedValue !== 0 : undefined); + const isSome = ret !== undefined; + bjs["swift_js_return_optional_bool"](isSome ? 1 : 0, isSome ? (ret ? 1 : 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripIntOrNull"] = function bjs_WithOptionalJSClass_roundTripIntOrNull(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripIntOrNull(valueIsSome ? valueWrappedValue : null); + const isSome = ret != null; + bjs["swift_js_return_optional_int"](isSome ? 1 : 0, isSome ? (ret | 0) : 0); + } catch (error) { + setException(error); + } + } + TestModule["bjs_WithOptionalJSClass_roundTripIntOrUndefined"] = function bjs_WithOptionalJSClass_roundTripIntOrUndefined(self, valueIsSome, valueWrappedValue) { + try { + let ret = swift.memory.getObject(self).roundTripIntOrUndefined(valueIsSome ? valueWrappedValue : undefined); + const isSome = ret !== undefined; + bjs["swift_js_return_optional_int"](isSome ? 1 : 0, isSome ? (ret | 0) : 0); + } catch (error) { + setException(error); + } + } }, setInstance: (i) => { instance = i; From 966b2d71b9b9f85d04f66a58cc79ef40a0c55d3f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 4 Feb 2026 14:21:26 +0900 Subject: [PATCH 6/6] ./Utilities/format.swift --- .../Inputs/MacroSwift/Optionals.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift index 3fabe55c..57d99451 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift @@ -99,14 +99,18 @@ func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active @JSGetter var stringOrUndefined: JSUndefinedOr @JSSetter func setStringOrUndefined(_ value: JSUndefinedOr) throws(JSException) @JSFunction func roundTripStringOrNull(value: String?) throws(JSException) -> String? - @JSFunction func roundTripStringOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + @JSFunction func roundTripStringOrUndefined( + value: JSUndefinedOr + ) throws(JSException) -> JSUndefinedOr @JSGetter var doubleOrNull: Double? @JSSetter func setDoubleOrNull(_ value: Double?) throws(JSException) @JSGetter var doubleOrUndefined: JSUndefinedOr @JSSetter func setDoubleOrUndefined(_ value: JSUndefinedOr) throws(JSException) @JSFunction func roundTripDoubleOrNull(value: Double?) throws(JSException) -> Double? - @JSFunction func roundTripDoubleOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr + @JSFunction func roundTripDoubleOrUndefined( + value: JSUndefinedOr + ) throws(JSException) -> JSUndefinedOr @JSGetter var boolOrNull: Bool? @JSSetter func setBoolOrNull(_ value: Bool?) throws(JSException) @@ -121,4 +125,4 @@ func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active @JSSetter func setIntOrUndefined(_ value: JSUndefinedOr) throws(JSException) @JSFunction func roundTripIntOrNull(value: Int?) throws(JSException) -> Int? @JSFunction func roundTripIntOrUndefined(value: JSUndefinedOr) throws(JSException) -> JSUndefinedOr -} \ No newline at end of file +}