diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index cbec4ba31..01dd419e7 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -123,23 +123,23 @@ public struct ClosureCodegen { for (index, paramType) in signature.parameters.enumerated() { let paramName = "param\(index)" - let liftInfo = try paramType.liftParameterInfo() + let liftParams = try paramType.liftParameterInfo() - for (argName, wasmType) in liftInfo.parameters { + for (argName, wasmType) in liftParams { let fullName = - liftInfo.parameters.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName + liftParams.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName abiParams.append((fullName, wasmType)) } - let argNames = liftInfo.parameters.map { (argName, _) in - liftInfo.parameters.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName + let argNames = liftParams.map { (argName, _) in + liftParams.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName } liftedParams.append("\(paramType.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))") } let closureCallExpr = ExprSyntax("closure(\(raw: liftedParams.joined(separator: ", ")))") - let abiReturnWasmType = try signature.returnType.loweringReturnInfo().returnType + let abiReturnWasmType = try signature.returnType.loweringReturnInfo() // Build signature using SwiftSignatureBuilder let funcSignature = SwiftSignatureBuilder.buildABIFunctionSignature( diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 207f6ee69..189844c5b 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -20,23 +20,17 @@ import BridgeJSUtilities public class ExportSwift { let progress: ProgressReporting let moduleName: String - private let exposeToGlobal: Bool private var skeleton: ExportedSkeleton - private var sourceFiles: [(sourceFile: SourceFileSyntax, inputFilePath: String)] = [] public init(progress: ProgressReporting, moduleName: String, skeleton: ExportedSkeleton) { self.progress = progress self.moduleName = moduleName - self.exposeToGlobal = skeleton.exposeToGlobal self.skeleton = skeleton } /// Finalizes the export process and generates the bridge code /// - /// - Parameters: - /// - exposeToGlobal: Whether to expose exported APIs to the global namespace (default: false) - /// - Returns: A tuple containing the generated Swift code and a skeleton - /// describing the exported APIs + /// - Returns: The generated Swift glue code, or nil if skeleton is empty public func finalize() throws -> String? { guard let outputSwift = try renderSwiftGlue() else { return nil @@ -101,7 +95,6 @@ public class ExportSwift { var parameters: [Parameter] = [] var abiParameterSignatures: [(name: String, type: WasmCoreType)] = [] var abiReturnType: WasmCoreType? - var externDecls: [DeclSyntax] = [] let effects: Effects init(effects: Effects) { @@ -119,12 +112,12 @@ public class ExportSwift { func liftParameter(param: Parameter) throws { parameters.append(param) - let liftingInfo = try param.type.liftParameterInfo() + let liftingParams = try param.type.liftParameterInfo() let argumentsToLift: [String] - if liftingInfo.parameters.count == 1 { + if liftingParams.count == 1 { argumentsToLift = [param.name] } else { - argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter } + argumentsToLift = liftingParams.map { (name, _) in param.name + name.capitalizedFirstLetter } } let typeNameForIntrinsic: String @@ -134,50 +127,37 @@ public class ExportSwift { case .closure(let signature, _): typeNameForIntrinsic = param.type.swiftType liftingExpr = ExprSyntax("_BJS_Closure_\(raw: signature.mangleName).bridgeJSLift(\(raw: param.name))") - case .swiftStruct(let structName): - typeNameForIntrinsic = structName - liftingExpr = ExprSyntax("\(raw: structName).bridgeJSLiftParameter()") case .array: typeNameForIntrinsic = param.type.swiftType liftingExpr = StackCodegen().liftExpression(for: param.type) case .nullable(let wrappedType, let kind): - let optionalSwiftType: String - if case .null = kind { - optionalSwiftType = "Optional" - } else { - optionalSwiftType = "JSUndefinedOr" - } - typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.swiftType)>" + let optionalSwiftType: String = + if case .null = kind { "Optional" } else { "JSUndefinedOr" } + let wrappedName: String = + if case .cast(let name) = wrappedType.descriptor.accessorTransform { name } else { + wrappedType.swiftType + } + typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedName)>" liftingExpr = ExprSyntax( "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" ) default: - typeNameForIntrinsic = param.type.swiftType + if case .cast(let name) = param.type.descriptor.accessorTransform { + typeNameForIntrinsic = name + } else { + typeNameForIntrinsic = param.type.swiftType + } liftingExpr = ExprSyntax( "\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))" ) } liftedParameterExprs.append(liftingExpr) - for (name, type) in zip(argumentsToLift, liftingInfo.parameters.map { $0.type }) { + for (name, type) in zip(argumentsToLift, liftingParams.map { $0.type }) { abiParameterSignatures.append((name, type)) } } - private func protocolCastSuffix(for returnType: BridgeType) -> (prefix: String, suffix: String) { - switch returnType { - case .swiftProtocol: - return ("", " as! \(returnType.swiftType)") - case .nullable(let wrappedType, _): - if case .swiftProtocol = wrappedType { - return ("(", ").flatMap { $0 as? \(wrappedType.swiftType) }") - } - return ("", "") - default: - return ("", "") - } - } - private func removeFirstLiftedParameter() -> (parameter: Parameter, expr: ExprSyntax) { let parameter = parameters.removeFirst() let expr = liftedParameterExprs.removeFirst() @@ -211,10 +191,9 @@ public class ExportSwift { if returnType == .void { return CodeBlockItemSyntax(item: .init(ExpressionStmtSyntax(expression: callExpr))) } else { - let (prefix, suffix) = protocolCastSuffix(for: returnType) - return CodeBlockItemSyntax( - item: .init(DeclSyntax("let ret = \(raw: prefix)\(raw: callExpr)\(raw: suffix)")) - ) + let binding = returnType.descriptor.accessorTransform + .applyToReturnBinding("\(callExpr)", isOptional: returnType.isOptional) + return CodeBlockItemSyntax(item: .init(DeclSyntax("\(raw: binding)"))) } } @@ -228,8 +207,9 @@ public class ExportSwift { if returnType == .void { append("\(raw: name)") } else { - let (prefix, suffix) = protocolCastSuffix(for: returnType) - append("let ret = \(raw: prefix)\(raw: name)\(raw: suffix)") + let binding = returnType.descriptor.accessorTransform + .applyToReturnBinding(name, isOptional: returnType.isOptional) + append("\(raw: binding)") } } @@ -246,14 +226,7 @@ public class ExportSwift { /// Generates intermediate variables for stack-using parameters if needed for LIFO compatibility private func generateParameterLifting() { let stackParamIndices = parameters.enumerated().compactMap { index, param -> Int? in - switch param.type { - case .swiftStruct, .nullable(.swiftStruct, _), - .associatedValueEnum, .nullable(.associatedValueEnum, _), - .array: - return index - default: - return nil - } + param.type.descriptor.usesStackLifting ? index : nil } guard stackParamIndices.count > 1 else { return } @@ -270,11 +243,13 @@ public class ExportSwift { func callPropertyGetter(propertyName: String, returnType: BridgeType) { let (_, selfExpr) = removeFirstLiftedParameter() + let expr = "\(selfExpr).\(propertyName)" if returnType == .void { - append("\(raw: selfExpr).\(raw: propertyName)") + append("\(raw: expr)") } else { - let (prefix, suffix) = protocolCastSuffix(for: returnType) - append("let ret = \(raw: prefix)\(raw: selfExpr).\(raw: propertyName)\(raw: suffix)") + let binding = returnType.descriptor.accessorTransform + .applyToReturnBinding(expr, isOptional: returnType.isOptional) + append("\(raw: binding)") } } @@ -299,8 +274,7 @@ public class ExportSwift { } private func _lowerReturnValue(returnType: BridgeType) throws { - let loweringInfo = try returnType.loweringReturnInfo() - abiReturnType = loweringInfo.returnType + abiReturnType = try returnType.loweringReturnInfo() if returnType == .void { return } @@ -736,10 +710,6 @@ struct StackCodegen { /// - Returns: An ExprSyntax representing the lift expression func liftExpression(for type: BridgeType) -> ExprSyntax { switch type { - case .string, .int, .uint, .bool, .float, .double, - .jsObject(nil), .jsValue, .swiftStruct, .swiftHeapObject, .unsafePointer, - .swiftProtocol, .caseEnum, .associatedValueEnum, .rawValueEnum: - return "\(raw: type.swiftType).bridgeJSStackPop()" case .jsObject(let className?): return "\(raw: className)(unsafelyWrapping: JSObject.bridgeJSStackPop())" case .nullable(let wrappedType, let kind): @@ -748,24 +718,38 @@ struct StackCodegen { return liftArrayExpression(elementType: elementType) case .dictionary(let valueType): return liftDictionaryExpression(valueType: valueType) - case .closure: - return "JSObject.bridgeJSStackPop()" case .void, .namespaceEnum: return "()" + default: + return "\(raw: type.swiftType).bridgeJSStackPop()" } } func liftArrayExpression(elementType: BridgeType) -> ExprSyntax { - switch elementType { - case .jsObject(let className?) where className != "JSObject": + if case .jsObject(let className?) = elementType, className != "JSObject" { return "[JSObject].bridgeJSStackPop().map { \(raw: className)(unsafelyWrapping: $0) }" - case .nullable, .closure: + } + if elementType.needsInlineCollectionHandling { return liftArrayExpressionInline(elementType: elementType) - case .void, .namespaceEnum: - fatalError("Invalid array element type: \(elementType)") - default: - return "[\(raw: elementType.swiftType)].bridgeJSStackPop()" } + return "[\(raw: elementType.swiftType)].bridgeJSStackPop()" + } + + func liftDictionaryExpression(valueType: BridgeType) -> ExprSyntax { + if case .jsObject(let className?) = valueType, className != "JSObject" { + return """ + { + let __dict = [String: JSObject].bridgeJSStackPop() + return __dict.mapValues { \(raw: className)(unsafelyWrapping: $0) } + }() + """ + } + if valueType.needsInlineCollectionHandling { + return liftDictionaryExpressionInline(valueType: valueType) + } + if case .void = valueType { fatalError("Invalid dictionary value type: \(valueType)") } + if case .namespaceEnum = valueType { fatalError("Invalid dictionary value type: \(valueType)") } + return "[String: \(raw: valueType.swiftType)].bridgeJSStackPop()" } private func liftArrayExpressionInline(elementType: BridgeType) -> ExprSyntax { @@ -785,24 +769,6 @@ struct StackCodegen { """ } - func liftDictionaryExpression(valueType: BridgeType) -> ExprSyntax { - switch valueType { - case .jsObject(let className?) where className != "JSObject": - return """ - { - let __dict = [String: JSObject].bridgeJSStackPop() - return __dict.mapValues { \(raw: className)(unsafelyWrapping: $0) } - }() - """ - case .nullable, .closure: - return liftDictionaryExpressionInline(valueType: valueType) - case .void, .namespaceEnum: - fatalError("Invalid dictionary value type: \(valueType)") - default: - return "[String: \(raw: valueType.swiftType)].bridgeJSStackPop()" - } - } - private func liftDictionaryExpressionInline(valueType: BridgeType) -> ExprSyntax { let valueLift = liftExpression(for: valueType) let swiftTypeName = valueType.swiftType @@ -824,14 +790,10 @@ struct StackCodegen { 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), .jsValue, - .swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum, - .array, .dictionary: - return "\(raw: typeName)<\(raw: wrappedType.swiftType)>.bridgeJSStackPop()" - case .jsObject(let className?): + case .jsObject(let className?) where className != "JSObject": return "\(raw: typeName).bridgeJSStackPop().map { \(raw: className)(unsafelyWrapping: $0) }" - case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol: - fatalError("Invalid nullable wrapped type: \(wrappedType)") + default: + return "\(raw: typeName)<\(raw: wrappedType.swiftType)>.bridgeJSStackPop()" } } @@ -847,24 +809,23 @@ struct StackCodegen { varPrefix: String ) -> [CodeBlockItemSyntax] { switch type { - case .string, .int, .uint, .bool, .float, .double, .jsValue, - .jsObject(nil), .swiftHeapObject, .unsafePointer, .closure, - .caseEnum, .rawValueEnum: - return ["\(raw: accessor).bridgeJSStackPush()"] - case .jsObject(_?): - return ["\(raw: accessor).jsObject.bridgeJSStackPush()"] - case .swiftProtocol: - return ["(\(raw: accessor) as! \(raw: type.swiftType)).bridgeJSStackPush()"] - case .associatedValueEnum, .swiftStruct: - return ["\(raw: accessor).bridgeJSStackPush()"] case .nullable(let wrappedType, _): return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix) - case .void, .namespaceEnum: - return [] case .array(let elementType): return lowerArrayStatements(elementType: elementType, accessor: accessor, varPrefix: varPrefix) case .dictionary(let valueType): return lowerDictionaryStatements(valueType: valueType, accessor: accessor, varPrefix: varPrefix) + default: + break + } + + let desc = type.descriptor + let transformed = desc.accessorTransform.apply(accessor) + switch desc.lowerMethod { + case .stackReturn, .fullReturn, .pushParameter: + return ["\(raw: transformed).bridgeJSStackPush()"] + case .none: + return [] } } @@ -873,22 +834,32 @@ struct StackCodegen { accessor: String, varPrefix: String ) -> [CodeBlockItemSyntax] { - switch elementType { - case .jsObject(let className?) where className != "JSObject": - return ["\(raw: accessor).map { $0.jsObject }.bridgeJSStackPush()"] - case .swiftProtocol: - return ["\(raw: accessor).map { $0 as! \(raw: elementType.swiftType) }.bridgeJSStackPush()"] - case .nullable, .closure: - return lowerArrayStatementsInline( - elementType: elementType, - accessor: accessor, - varPrefix: varPrefix - ) - case .void, .namespaceEnum: - fatalError("Invalid array element type: \(elementType)") - default: - return ["\(raw: accessor).bridgeJSStackPush()"] + if elementType.needsInlineCollectionHandling { + return lowerArrayStatementsInline(elementType: elementType, accessor: accessor, varPrefix: varPrefix) } + return lowerCollectionSimple(elementType: elementType, accessor: accessor, mapMethod: "map") + } + + private func lowerDictionaryStatements( + valueType: BridgeType, + accessor: String, + varPrefix: String + ) -> [CodeBlockItemSyntax] { + if valueType.needsInlineCollectionHandling { + return lowerDictionaryStatementsInline(valueType: valueType, accessor: accessor, varPrefix: varPrefix) + } + return lowerCollectionSimple(elementType: valueType, accessor: accessor, mapMethod: "mapValues") + } + + private func lowerCollectionSimple( + elementType: BridgeType, + accessor: String, + mapMethod: String + ) -> [CodeBlockItemSyntax] { + if let mapClosure = elementType.descriptor.accessorTransform.mapClosure { + return ["\(raw: accessor).\(raw: mapMethod) { \(raw: mapClosure) }.bridgeJSStackPush()"] + } + return ["\(raw: accessor).bridgeJSStackPush()"] } private func lowerArrayStatementsInline( @@ -915,29 +886,6 @@ struct StackCodegen { return Array(parsed) } - private func lowerDictionaryStatements( - valueType: BridgeType, - accessor: String, - varPrefix: String - ) -> [CodeBlockItemSyntax] { - switch valueType { - case .jsObject(let className?) where className != "JSObject": - return ["\(raw: accessor).mapValues { $0.jsObject }.bridgeJSStackPush()"] - case .swiftProtocol: - return ["\(raw: accessor).mapValues { $0 as! \(raw: valueType.swiftType) }.bridgeJSStackPush()"] - case .nullable, .closure: - return lowerDictionaryStatementsInline( - valueType: valueType, - accessor: accessor, - varPrefix: varPrefix - ) - case .void, .namespaceEnum: - fatalError("Invalid dictionary value type: \(valueType)") - default: - return ["\(raw: accessor).bridgeJSStackPush()"] - } - } - private func lowerDictionaryStatementsInline( valueType: BridgeType, accessor: String, @@ -977,11 +925,8 @@ struct StackCodegen { accessor: String, varPrefix: String ) -> [CodeBlockItemSyntax] { - switch wrappedType { - case .array, .dictionary, .swiftStruct: + if wrappedType.descriptor.optionalConvention == .stackABI { return ["\(raw: accessor).bridgeJSStackPush()"] - default: - break } var statements: [String] = [] @@ -1006,16 +951,12 @@ struct StackCodegen { wrappedType: BridgeType, unwrappedVar: String ) -> [CodeBlockItemSyntax] { - switch wrappedType { - case .jsObject(_?): - return ["\(raw: unwrappedVar).jsObject.bridgeJSStackPush()"] - case .swiftProtocol: - return ["(\(raw: unwrappedVar) as! \(raw: wrappedType.swiftType)).bridgeJSStackPush()"] - case .string, .int, .uint, .bool, .float, .double, .jsValue, - .jsObject(nil), .swiftHeapObject, .unsafePointer, .closure, - .caseEnum, .rawValueEnum, .associatedValueEnum: - return ["\(raw: unwrappedVar).bridgeJSStackPush()"] - default: + let desc = wrappedType.descriptor + let transformed = desc.accessorTransform.apply(unwrappedVar) + switch desc.lowerMethod { + case .stackReturn, .fullReturn, .pushParameter: + return ["\(raw: transformed).bridgeJSStackPush()"] + case .none: return ["preconditionFailure(\"BridgeJS: unsupported optional wrapped type\")"] } } @@ -1603,137 +1544,31 @@ extension BridgeType { } } - var isClosureType: Bool { - if case .closure = self { return true } - return false - } - - struct LiftingIntrinsicInfo: Sendable { - let parameters: [(name: String, type: WasmCoreType)] - - static let bool = LiftingIntrinsicInfo(parameters: [("value", .i32)]) - static let int = LiftingIntrinsicInfo(parameters: [("value", .i32)]) - static let float = LiftingIntrinsicInfo(parameters: [("value", .f32)]) - static let double = LiftingIntrinsicInfo(parameters: [("value", .f64)]) - static let string = LiftingIntrinsicInfo(parameters: [("bytes", .i32), ("length", .i32)]) - static let jsObject = LiftingIntrinsicInfo(parameters: [("value", .i32)]) - static let jsValue = LiftingIntrinsicInfo(parameters: [("kind", .i32), ("payload1", .i32), ("payload2", .f64)]) - static let swiftHeapObject = LiftingIntrinsicInfo(parameters: [("value", .pointer)]) - static let unsafePointer = LiftingIntrinsicInfo(parameters: [("pointer", .pointer)]) - static let void = LiftingIntrinsicInfo(parameters: []) - static let caseEnum = LiftingIntrinsicInfo(parameters: [("value", .i32)]) - static let associatedValueEnum = LiftingIntrinsicInfo(parameters: [ - ("caseId", .i32) - ]) - } - - func liftParameterInfo() throws -> LiftingIntrinsicInfo { + func liftParameterInfo() throws -> [(name: String, type: WasmCoreType)] { switch self { - case .bool: return .bool - case .int, .uint: return .int - case .float: return .float - case .double: return .double - case .string: return .string - case .jsObject: return .jsObject - case .jsValue: return .jsValue - case .swiftHeapObject: return .swiftHeapObject - case .unsafePointer: return .unsafePointer - case .swiftProtocol: return .jsObject - case .void: return .void case .nullable(let wrappedType, _): - let wrappedInfo = try wrappedType.liftParameterInfo() - if wrappedInfo.parameters.isEmpty { - return LiftingIntrinsicInfo(parameters: []) + let wrappedParams = try wrappedType.liftParameterInfo() + if wrappedParams.isEmpty { + return [] } var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)] - optionalParams.append(contentsOf: wrappedInfo.parameters) - return LiftingIntrinsicInfo(parameters: optionalParams) - case .caseEnum: return .caseEnum - case .rawValueEnum(_, let rawType): - return rawType.liftingIntrinsicInfo - case .associatedValueEnum: - return .associatedValueEnum - case .swiftStruct: - return LiftingIntrinsicInfo(parameters: []) + optionalParams.append(contentsOf: wrappedParams) + return optionalParams case .namespaceEnum: throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") - case .closure: - return LiftingIntrinsicInfo(parameters: [("callbackId", .i32)]) - case .array, .dictionary: - return LiftingIntrinsicInfo(parameters: []) + default: + return descriptor.wasmParams } } - struct LoweringIntrinsicInfo: Sendable { - let returnType: WasmCoreType? - - static let bool = LoweringIntrinsicInfo(returnType: .i32) - static let int = LoweringIntrinsicInfo(returnType: .i32) - static let float = LoweringIntrinsicInfo(returnType: .f32) - static let double = LoweringIntrinsicInfo(returnType: .f64) - static let string = LoweringIntrinsicInfo(returnType: nil) - static let jsObject = LoweringIntrinsicInfo(returnType: .i32) - static let jsValue = LoweringIntrinsicInfo(returnType: nil) - static let swiftHeapObject = LoweringIntrinsicInfo(returnType: .pointer) - static let unsafePointer = LoweringIntrinsicInfo(returnType: .pointer) - static let void = LoweringIntrinsicInfo(returnType: nil) - static let caseEnum = LoweringIntrinsicInfo(returnType: .i32) - static let rawValueEnum = LoweringIntrinsicInfo(returnType: .i32) - static let associatedValueEnum = LoweringIntrinsicInfo(returnType: nil) - static let swiftStruct = LoweringIntrinsicInfo(returnType: nil) - static let optional = LoweringIntrinsicInfo(returnType: nil) - static let array = LoweringIntrinsicInfo(returnType: nil) - } - - func loweringReturnInfo() throws -> LoweringIntrinsicInfo { + func loweringReturnInfo() throws -> WasmCoreType? { switch self { - case .bool: return .bool - case .int, .uint: return .int - case .float: return .float - case .double: return .double - case .string: return .string - case .jsObject: return .jsObject - case .jsValue: return .jsValue - case .swiftHeapObject: return .swiftHeapObject - case .unsafePointer: return .unsafePointer - case .swiftProtocol: return .jsObject - case .void: return .void - case .nullable: return .optional - case .caseEnum: return .caseEnum - case .rawValueEnum(_, let rawType): - return rawType.loweringIntrinsicInfo - case .associatedValueEnum: - return .associatedValueEnum - case .swiftStruct: - return .swiftStruct + case .nullable: + return nil case .namespaceEnum: throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters") - case .closure: - return .jsObject - case .array, .dictionary: - return .array - } - } -} - -extension SwiftEnumRawType { - var liftingIntrinsicInfo: BridgeType.LiftingIntrinsicInfo { - switch self { - case .bool: return .bool - case .int, .int32, .int64, .uint, .uint32, .uint64: return .int - case .float: return .float - case .double: return .double - case .string: return .string - } - } - - var loweringIntrinsicInfo: BridgeType.LoweringIntrinsicInfo { - switch self { - case .bool: return .bool - case .int, .int32, .int64, .uint, .uint32, .uint64: return .int - case .float: return .float - case .double: return .double - case .string: return .string + default: + return descriptor.wasmReturnType } } } @@ -1765,17 +1600,6 @@ extension DeclModifierSyntax { } } - var isAtLeastPackage: Bool { - switch self.name.tokenKind { - case .keyword(.private): false - case .keyword(.fileprivate): false - case .keyword(.internal): true - case .keyword(.package): true - case .keyword(.public): true - case .keyword(.open): true - default: false - } - } } extension WithModifiersSyntax { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 8eead20b7..078741751 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -103,31 +103,27 @@ public struct ImportTS { } func lowerParameter(param: Parameter) throws { - let loweringInfo = try param.type.loweringParameterInfo(context: context) + let loweredParams = try param.type.loweringParameterInfo(context: context) switch param.type { case .closure(let signature, useJSTypedClosure: false): let jsTypedClosureType = BridgeType.closure(signature, useJSTypedClosure: true).swiftType body.write("let \(param.name) = \(jsTypedClosureType)(\(param.name))") - // The just created JSObject is not owned by the caller unlike those passed in parameters, - // so we need to extend its lifetime during the call to ensure the JSObject.id is valid. valuesToExtendLifetimeDuringCall.append(param.name) default: break } let initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()") - if loweringInfo.loweredParameters.isEmpty { + if loweredParams.isEmpty { stackLoweringStmts.insert("let _ = \(initializerExpr)", at: 0) return } - // Generate destructured variable names for all lowered parameters - let destructuredNames = loweringInfo.loweredParameters.map { + let destructuredNames = loweredParams.map { "\(param.name)\($0.name.capitalizedFirstLetter)" } - // Always add destructuring statement to body (unified for single and multiple) let pattern: String if destructuredNames.count == 1 { pattern = destructuredNames[0] @@ -138,31 +134,26 @@ public struct ImportTS { body.write("let \(pattern) = \(initializerExpr)") destructuredVarNames.append(contentsOf: destructuredNames) - // Add to signatures and forwardings (unified for both single and multiple) - for (index, (paramName, type)) in loweringInfo.loweredParameters.enumerated() { - // For single parameter, use param.name; for multiple, use constructed name + for (index, (paramName, type)) in loweredParams.enumerated() { let abiParamName: String - if loweringInfo.loweredParameters.count == 1 { + if loweredParams.count == 1 { abiParamName = param.name } else { abiParamName = "\(param.name)\(paramName.capitalizedFirstLetter)" } abiParameterSignatures.append((abiParamName, type)) - - // Always use destructured variable in call without labels - // Swift allows omitting labels when they match parameter names abiParameterForwardings.append(destructuredNames[index]) } } func call(returnType: BridgeType) throws { - let liftingInfo: BridgeType.LiftingReturnInfo = try returnType.liftingReturnInfo(context: context) + let liftingReturn = try returnType.liftingReturnType(context: context) for stmt in stackLoweringStmts { body.write(stmt.description) } let assign = - (returnType == .void || returnType.usesSideChannelForOptionalReturn() || liftingInfo.valueToLift == nil) + (returnType == .void || returnType.usesSideChannelForOptionalReturn || liftingReturn == nil) ? "" : "let ret = " let callExpr = "\(abiName)(\(abiParameterForwardings.joined(separator: ", ")))" @@ -185,25 +176,24 @@ public struct ImportTS { } func liftReturnValue(returnType: BridgeType) throws { - let liftingInfo = try returnType.liftingReturnInfo(context: context) + let liftingReturn = try returnType.liftingReturnType(context: context) if returnType == .void { abiReturnType = nil return } - if returnType.usesSideChannelForOptionalReturn() { - // Side channel returns: extern function returns Void, value is retrieved via side channel + if returnType.usesSideChannelForOptionalReturn { abiReturnType = nil body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()") } else { - abiReturnType = liftingInfo.valueToLift + abiReturnType = liftingReturn let liftExpr: String switch returnType { case .closure(let signature, _): liftExpr = "_BJS_Closure_\(signature.mangleName).bridgeJSLift(ret)" default: - if liftingInfo.valueToLift != nil { + if liftingReturn != nil { liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn(ret)" } else { liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn()" @@ -693,145 +683,42 @@ enum SwiftCodePattern { } extension BridgeType { - struct LoweringParameterInfo { - let loweredParameters: [(name: String, type: WasmCoreType)] - - static let bool = LoweringParameterInfo(loweredParameters: [("value", .i32)]) - static let int = LoweringParameterInfo(loweredParameters: [("value", .i32)]) - static let float = LoweringParameterInfo(loweredParameters: [("value", .f32)]) - static let double = LoweringParameterInfo(loweredParameters: [("value", .f64)]) - static let string = LoweringParameterInfo(loweredParameters: [("value", .i32)]) - static let jsObject = LoweringParameterInfo(loweredParameters: [("value", .i32)]) - static let jsValue = LoweringParameterInfo(loweredParameters: [ - ("kind", .i32), - ("payload1", .i32), - ("payload2", .f64), - ]) - static let void = LoweringParameterInfo(loweredParameters: []) - } - - func loweringParameterInfo(context: BridgeContext = .importTS) throws -> LoweringParameterInfo { + func loweringParameterInfo(context: BridgeContext = .importTS) throws -> [(name: String, type: WasmCoreType)] { switch self { - case .bool: return .bool - case .int, .uint: return .int - case .float: return .float - case .double: return .double - case .string: return .string - case .jsObject: return .jsObject - case .jsValue: return .jsValue - case .void: return .void - case .closure: - // Swift closure is passed to JS as a JS function reference. - return LoweringParameterInfo(loweredParameters: [("funcRef", .i32)]) - case .unsafePointer: - return LoweringParameterInfo(loweredParameters: [("pointer", .pointer)]) - case .swiftHeapObject: - return LoweringParameterInfo(loweredParameters: [("pointer", .pointer)]) + case .nullable(let wrappedType, _): + let wrappedParams = try wrappedType.loweringParameterInfo(context: context) + return [("isSome", WasmCoreType.i32)] + wrappedParams + case .namespaceEnum: + throw BridgeJSCoreError("Namespace enums cannot be used as parameters") case .swiftProtocol: throw BridgeJSCoreError("swiftProtocol is not supported in imported signatures") - case .caseEnum: - switch context { - case .importTS: - throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LoweringParameterInfo(loweredParameters: [("value", .i32)]) - } - case .rawValueEnum(_, let rawType): - let wasmType = rawType.wasmCoreType ?? .i32 - return LoweringParameterInfo(loweredParameters: [("value", wasmType)]) - case .associatedValueEnum: - switch context { - case .importTS: + case .caseEnum, .associatedValueEnum: + if context == .importTS { throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LoweringParameterInfo(loweredParameters: [("caseId", .i32)]) } - case .swiftStruct: - switch context { - case .importTS: - // Swift structs are bridged as JS objects (object IDs) in imported signatures. - return LoweringParameterInfo(loweredParameters: [("objectId", .i32)]) - case .exportSwift: - return LoweringParameterInfo(loweredParameters: []) - } - case .namespaceEnum: - throw BridgeJSCoreError("Namespace enums cannot be used as parameters") - 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 .array, .dictionary: - return LoweringParameterInfo(loweredParameters: []) + fallthrough + default: + return descriptor.importParams } } - struct LiftingReturnInfo { - let valueToLift: WasmCoreType? - - static let bool = LiftingReturnInfo(valueToLift: .i32) - static let int = LiftingReturnInfo(valueToLift: .i32) - static let float = LiftingReturnInfo(valueToLift: .f32) - static let double = LiftingReturnInfo(valueToLift: .f64) - static let string = LiftingReturnInfo(valueToLift: .i32) - static let jsObject = LiftingReturnInfo(valueToLift: .i32) - static let jsValue = LiftingReturnInfo(valueToLift: nil) - static let void = LiftingReturnInfo(valueToLift: nil) - } - - func liftingReturnInfo( + func liftingReturnType( context: BridgeContext = .importTS - ) throws -> LiftingReturnInfo { + ) throws -> WasmCoreType? { switch self { - case .bool: return .bool - case .int, .uint: return .int - case .float: return .float - case .double: return .double - case .string: return .string - case .jsObject: return .jsObject - case .jsValue: return .jsValue - case .void: return .void - case .closure: - // JS returns a callback ID for closures, which Swift lifts to a typed closure. - return LiftingReturnInfo(valueToLift: .i32) - case .unsafePointer: - return LiftingReturnInfo(valueToLift: .pointer) - case .swiftHeapObject: - return LiftingReturnInfo(valueToLift: .pointer) + case .nullable(let wrappedType, _): + return try wrappedType.liftingReturnType(context: context) + case .namespaceEnum: + throw BridgeJSCoreError("Namespace enums cannot be used as return values") case .swiftProtocol: throw BridgeJSCoreError("swiftProtocol is not supported in imported signatures") - case .caseEnum: - switch context { - case .importTS: - throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LiftingReturnInfo(valueToLift: .i32) - } - case .rawValueEnum(_, let rawType): - let wasmType = rawType.wasmCoreType ?? .i32 - return LiftingReturnInfo(valueToLift: wasmType) - case .associatedValueEnum: - switch context { - case .importTS: + case .caseEnum, .associatedValueEnum: + if context == .importTS { throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports") - case .exportSwift: - return LiftingReturnInfo(valueToLift: .i32) - } - case .swiftStruct: - switch context { - case .importTS: - // Swift structs are bridged as JS objects (object IDs) in imported signatures. - return LiftingReturnInfo(valueToLift: .i32) - case .exportSwift: - return LiftingReturnInfo(valueToLift: nil) } - case .namespaceEnum: - throw BridgeJSCoreError("Namespace enums cannot be used as return values") - case .nullable(let wrappedType, _): - let wrappedInfo = try wrappedType.liftingReturnInfo(context: context) - return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift) - case .array, .dictionary: - return LiftingReturnInfo(valueToLift: nil) + fallthrough + default: + return descriptor.importReturnType } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index b779e0171..e07d2504e 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -364,8 +364,9 @@ public struct BridgeJSLink { printer.write( "const bytes = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, ptr, len)\(sharedMemory ? ".slice()" : "");" ) - printer.write("const value = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);") - printer.write("\(JSGlueVariableScope.reservedStringStack).push(value);") + printer.write( + "\(JSGlueVariableScope.reservedStringStack).push(\(JSGlueVariableScope.reservedTextDecoder).decode(bytes));" + ) } printer.write("}") printer.write("bjs[\"swift_js_pop_i32\"] = function() {") @@ -1068,7 +1069,7 @@ public struct BridgeJSLink { for skeleton in skeletons.compactMap(\.exported) { for enumDef in skeleton.enums where enumDef.enumType == .associatedValue { printer.write( - "const \(enumDef.name)Helpers = __bjs_create\(enumDef.valuesName)Helpers()();" + "const \(enumDef.name)Helpers = __bjs_create\(enumDef.valuesName)Helpers();" ) printer.write("\(JSGlueVariableScope.reservedEnumHelpers).\(enumDef.name) = \(enumDef.name)Helpers;") printer.nextLine() @@ -1084,7 +1085,7 @@ public struct BridgeJSLink { for skeleton in skeletons.compactMap(\.exported) { for structDef in skeleton.structs { printer.write( - "const \(structDef.name)Helpers = __bjs_create\(structDef.name)Helpers()();" + "const \(structDef.name)Helpers = __bjs_create\(structDef.name)Helpers();" ) printer.write( "\(JSGlueVariableScope.reservedStructHelpers).\(structDef.name) = \(structDef.name)Helpers;" @@ -1537,16 +1538,8 @@ public struct BridgeJSLink { let enumValuesName = enumDefinition.valuesName switch enumDefinition.enumType { - case .simple: - let fragment = IntrinsicJSFragment.simpleEnumHelper(enumDefinition: enumDefinition) - _ = try fragment.printCode([enumValuesName], context) - jsTopLevelLines.append(contentsOf: printer.lines) - case .rawValue: - guard enumDefinition.rawType != nil else { - throw BridgeJSLinkError(message: "Raw value enum \(enumDefinition.name) is missing rawType") - } - - let fragment = IntrinsicJSFragment.rawValueEnumHelper(enumDefinition: enumDefinition) + case .simple, .rawValue: + let fragment = IntrinsicJSFragment.caseEnumHelper(enumDefinition: enumDefinition) _ = try fragment.printCode([enumValuesName], context) jsTopLevelLines.append(contentsOf: printer.lines) case .associatedValue: @@ -2175,7 +2168,7 @@ extension BridgeJSLink { printer.write("} catch (error) {") printer.indent { printer.write("setException(error);") - if let abiReturnType = returnType.abiReturnType { + if !returnType.isOptional, let abiReturnType = returnType.descriptor.wasmReturnType { printer.write("return \(abiReturnType.placeholderValue)") } } @@ -2250,7 +2243,7 @@ extension BridgeJSLink { func callPropertyGetter(name: String, returnType: BridgeType) throws -> String? { let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)" let accessExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name) - if context == .exportSwift, returnType.usesSideChannelForOptionalReturn() { + if context == .exportSwift, returnType.usesSideChannelForOptionalReturn { guard case .nullable(let wrappedType, _) = returnType else { fatalError("usesSideChannelForOptionalReturn returned true for non-optional type") } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index da21a6e08..6154952ca 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -84,6 +84,12 @@ final class JSGlueVariableScope { return suffixedName } + func reserveNames(_ names: [String]) { + for name in names { + variables.insert(name) + } + } + func registerIntrinsic(_ name: String, build: (CodeFragmentPrinter) throws -> Void) rethrows { try intrinsicRegistry.register(name: name, build: build) } @@ -109,17 +115,7 @@ extension JSGlueVariableScope { printer.write("\(JSGlueVariableScope.reservedPointerStack).push(\(value));") } - // MARK: Return - - func emitPushI32Return(_ value: String, printer: CodeFragmentPrinter) { - printer.write("\(JSGlueVariableScope.reservedI32Stack).push(\(value));") - } - func emitPushF64Return(_ value: String, printer: CodeFragmentPrinter) { - printer.write("\(JSGlueVariableScope.reservedF64Stack).push(\(value));") - } - func emitPushPointerReturn(_ value: String, printer: CodeFragmentPrinter) { - printer.write("\(JSGlueVariableScope.reservedPointerStack).push(\(value));") - } + // MARK: Pop func popString() -> String { return "\(JSGlueVariableScope.reservedStringStack).pop()" } @@ -160,6 +156,10 @@ struct IntrinsicJSFragment: Sendable { } } + private static func emitCleanupCall(_ cleanupExpr: String, into printer: CodeFragmentPrinter) { + printer.write("\(cleanupExpr) && \(cleanupExpr)();") + } + /// A function that prints the fragment code. /// /// - Parameters: @@ -204,36 +204,7 @@ struct IntrinsicJSFragment: Sendable { } ) - /// NOTE: JavaScript engine itself converts booleans to integers when passing them to - /// Wasm functions, so we don't need to do anything here - static let boolLowerParameter = identity - static let boolLiftReturn = IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, _ in - return ["\(arguments[0]) !== 0"] - } - ) - static let boolLiftParameter = IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, _ in - return ["\(arguments[0]) !== 0"] - } - ) - static let boolLowerReturn = IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, _ in - return ["\(arguments[0]) ? 1 : 0"] - } - ) - - /// Convert signed Int32 to unsigned for UInt values - static let uintLiftReturn = IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, _ in - return ["\(arguments[0]) >>> 0"] - } - ) - static let uintLiftParameter = uintLiftReturn + // MARK: - String Fragments static let stringLowerParameter = IntrinsicJSFragment( parameters: ["value"], @@ -264,7 +235,9 @@ struct IntrinsicJSFragment: Sendable { let objectId = arguments[0] let objectLabel = scope.variable("\(objectId)Object") // TODO: Implement "take" operation - printer.write("const \(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));") + printer.write( + "const \(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));" + ) printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(objectId));") return [objectLabel] } @@ -280,37 +253,32 @@ struct IntrinsicJSFragment: Sendable { } ) + // MARK: - JSObject Fragments + static let jsObjectLowerParameter = IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, _ in return ["swift.memory.retain(\(arguments[0]))"] } ) - static let jsObjectLiftReturn = IntrinsicJSFragment( - parameters: ["retId"], - printCode: { arguments, context in - let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) - // TODO: Implement "take" operation - let resultLabel = scope.variable("ret") - let retId = arguments[0] - printer.write("const \(resultLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(retId));") - printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(retId));") - return [resultLabel] - } - ) - static let jsObjectLiftRetainedObjectId = IntrinsicJSFragment( - parameters: ["objectId"], - printCode: { arguments, context in - let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) - let resultLabel = scope.variable("value") - let objectId = arguments[0] - printer.write( - "const \(resultLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));" - ) - printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(objectId));") - return [resultLabel] - } - ) + private static func jsObjectTakeRetained(hint: String = "ret") -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: ["objectId"], + printCode: { arguments, context in + let (scope, printer, _) = (context.scope, context.printer, context.cleanupCode) + // TODO: Implement "take" operation + let resultLabel = scope.variable(hint) + let objectId = arguments[0] + printer.write( + "const \(resultLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));" + ) + printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(objectId));") + return [resultLabel] + } + ) + } + static let jsObjectLiftReturn = jsObjectTakeRetained(hint: "ret") + static let jsObjectLiftRetainedObjectId = jsObjectTakeRetained(hint: "value") static let jsObjectLiftParameter = IntrinsicJSFragment( parameters: ["objectId"], printCode: { arguments, _ in @@ -324,6 +292,8 @@ struct IntrinsicJSFragment: Sendable { } ) + // MARK: - JSValue Fragments + private static let jsValueLowerHelperName = "__bjs_jsValueLower" private static let jsValueLiftHelperName = "__bjs_jsValueLift" @@ -394,7 +364,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("case \"string\":") printer.indent { printer.write("\(kindVar) = 1;") - printer.write("\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) printer.write("\(payload2Var) = 0;") printer.write("break;") } @@ -408,28 +380,36 @@ struct IntrinsicJSFragment: Sendable { printer.write("case \"object\":") printer.indent { printer.write("\(kindVar) = 3;") - printer.write("\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) printer.write("\(payload2Var) = 0;") printer.write("break;") } printer.write("case \"function\":") printer.indent { printer.write("\(kindVar) = 3;") - printer.write("\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) printer.write("\(payload2Var) = 0;") printer.write("break;") } printer.write("case \"symbol\":") printer.indent { printer.write("\(kindVar) = 7;") - printer.write("\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) printer.write("\(payload2Var) = 0;") printer.write("break;") } printer.write("case \"bigint\":") printer.indent { printer.write("\(kindVar) = 8;") - printer.write("\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) printer.write("\(payload2Var) = 0;") printer.write("break;") } @@ -528,45 +508,20 @@ struct IntrinsicJSFragment: Sendable { } static func jsValueLowerReturn(context: BridgeContext) -> IntrinsicJSFragment { - switch context { - case .importTS: - // Return values from imported JS functions should be delivered to the Swift side - // via the parameter stacks that `_swift_js_pop_*` read from. - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let lowered = try jsValueLower.printCode(arguments, context) - let kindVar = lowered[0] - let payload1Var = lowered[1] - let payload2Var = lowered[2] - scope.emitPushI32Parameter(kindVar, printer: printer) - scope.emitPushI32Parameter(payload1Var, printer: printer) - scope.emitPushF64Parameter(payload2Var, printer: printer) - return [] - } - ) - case .exportSwift: - // Kept for symmetry, though JSValue return for export currently relies on Swift pushing - // to tmpRet stacks directly. - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let lowered = try jsValueLower.printCode( - arguments, - context - ) - let kindVar = lowered[0] - let payload1Var = lowered[1] - let payload2Var = lowered[2] - scope.emitPushI32Parameter(kindVar, printer: printer) - scope.emitPushI32Parameter(payload1Var, printer: printer) - scope.emitPushF64Parameter(payload2Var, printer: printer) - return [] - } - ) - } + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let lowered = try jsValueLower.printCode(arguments, context) + let kindVar = lowered[0] + let payload1Var = lowered[1] + let payload2Var = lowered[2] + scope.emitPushI32Parameter(kindVar, printer: printer) + scope.emitPushI32Parameter(payload1Var, printer: printer) + scope.emitPushF64Parameter(payload2Var, printer: printer) + return [] + } + ) } static let jsValueLift = IntrinsicJSFragment( @@ -600,12 +555,17 @@ struct IntrinsicJSFragment: Sendable { } ) - static let swiftHeapObjectLowerParameter = IntrinsicJSFragment( + // MARK: - SwiftHeapObject Fragments + + private static let swiftHeapObjectExtractPointer = IntrinsicJSFragment( parameters: ["value"], - printCode: { arguments, context in + printCode: { arguments, _ in return ["\(arguments[0]).pointer"] } ) + static let swiftHeapObjectLowerParameter = swiftHeapObjectExtractPointer + static let swiftHeapObjectLowerReturn = swiftHeapObjectExtractPointer + static func swiftHeapObjectLiftReturn(_ name: String) -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["value"], @@ -624,25 +584,21 @@ struct IntrinsicJSFragment: Sendable { } ) } - static let swiftHeapObjectLowerReturn = IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - return ["\(arguments[0]).pointer"] - } - ) + + // MARK: - Associated Enum Fragments static func associatedEnumLowerParameter(enumBase: String) -> IntrinsicJSFragment { IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in - let (printer, cleanup) = (context.printer, context.cleanupCode) + let (scope, printer, cleanup) = (context.scope, context.printer, context.cleanupCode) let value = arguments[0] - let caseIdName = "\(value)CaseId" - let cleanupName = "\(value)Cleanup" + let caseIdName = scope.variable("\(value)CaseId") + let cleanupName = scope.variable("\(value)Cleanup") printer.write( "const { caseId: \(caseIdName), cleanup: \(cleanupName) } = \(JSGlueVariableScope.reservedEnumHelpers).\(enumBase).lower(\(value));" ) - cleanup.write("if (\(cleanupName)) { \(cleanupName)(); }") + emitCleanupCall(cleanupName, into: cleanup) return [caseIdName] } ) @@ -662,435 +618,476 @@ struct IntrinsicJSFragment: Sendable { ) } - static func optionalLiftParameter(wrappedType: BridgeType, kind: JSOptionalKind) throws -> IntrinsicJSFragment { - if case .jsValue = wrappedType { + // MARK: - Optional Handling + + static func optionalLiftParameter( + wrappedType: BridgeType, + kind: JSOptionalKind, + context bridgeContext: BridgeContext = .importTS + ) throws -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, desc.wasmParams.count == 1 { return IntrinsicJSFragment( - parameters: ["isSome", "kind", "payload1", "payload2"], - printCode: { arguments, context in + parameters: ["isSome", "wrappedValue"], + printCode: { arguments, _ in let isSome = arguments[0] - let lifted = try jsValueLiftParameter.printCode( - [arguments[1], arguments[2], arguments[3]], - context - ) - let valueExpr = lifted.first ?? "undefined" - return ["\(isSome) ? \(valueExpr) : null"] + let wrappedValue = arguments[1] + let absenceLiteral = kind.absenceLiteral + if let coerce = glue.liftCoerce { + let coerced = coerce.replacingOccurrences(of: "$0", with: wrappedValue) + return ["\(isSome) ? \(coerced) : \(absenceLiteral)"] + } + return ["\(isSome) ? \(wrappedValue) : \(absenceLiteral)"] } ) } + let innerFragment = try liftParameter(type: wrappedType, context: bridgeContext) + return compositeOptionalLiftParameter( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment + ) + } + + private static func compositeOptionalLiftParameter( + wrappedType: BridgeType, + kind: JSOptionalKind, + innerFragment: IntrinsicJSFragment + ) -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + let isStackConvention = desc.optionalConvention == .stackABI + let absenceLiteral = kind.absenceLiteral + + let outerParams: [String] + if isStackConvention { + outerParams = ["isSome"] + } else { + outerParams = ["isSome"] + innerFragment.parameters + } + return IntrinsicJSFragment( - parameters: ["isSome", "wrappedValue"], + parameters: outerParams, printCode: { arguments, context in let (scope, printer) = (context.scope, context.printer) let isSome = arguments[0] - let wrappedValue = arguments[1] - let resultExpr: String - let absenceLiteral = kind.absenceLiteral - - switch wrappedType { - case .int, .float, .double, .caseEnum: - resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" - case .bool: - resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : \(absenceLiteral)" - case .string: - let objectLabel = scope.variable("obj") - printer.write("let \(objectLabel);") - printer.write("if (\(isSome)) {") - printer.indent { - printer.write( - "\(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue));" - ) - printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(wrappedValue));") - } - printer.write("}") - resultExpr = "\(isSome) ? \(objectLabel) : \(absenceLiteral)" - case .closure: - resultExpr = - "\(isSome) ? \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue)) : \(absenceLiteral)" - case .swiftHeapObject(let name): - resultExpr = "\(isSome) ? _exports['\(name)'].__construct(\(wrappedValue)) : \(absenceLiteral)" - case .jsObject: - resultExpr = - "\(isSome) ? \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue)) : \(absenceLiteral)" - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: - let objectLabel = scope.variable("obj") - printer.write("let \(objectLabel);") - printer.write("if (\(isSome)) {") - printer.indent { - printer.write( - "\(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(wrappedValue));" - ) - printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(wrappedValue));") - } - printer.write("}") - resultExpr = "\(isSome) ? \(objectLabel) : \(absenceLiteral)" - case .bool: - resultExpr = "\(isSome) ? \(wrappedValue) !== 0 : \(absenceLiteral)" - default: - resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" - } - case .associatedValueEnum(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let enumVar = scope.variable("enumValue") - printer.write("let \(enumVar);") - printer.write("if (\(isSome)) {") - printer.indent { - printer.write( - "\(enumVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(wrappedValue));" - ) - } - printer.write("}") - resultExpr = "\(isSome) ? \(enumVar) : \(absenceLiteral)" - case .swiftStruct(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let structVar = scope.variable("structValue") - printer.write("let \(structVar);") + let innerArgs = isStackConvention ? [] : Array(arguments.dropFirst()) + + let bufferPrinter = CodeFragmentPrinter() + let innerResults = try innerFragment.printCode( + innerArgs, + context.with(\.printer, bufferPrinter) + ) + + let hasSideEffects = !bufferPrinter.lines.isEmpty + let innerExpr = innerResults.first ?? "undefined" + + if hasSideEffects { + let resultVar = scope.variable("optResult") + printer.write("let \(resultVar);") printer.write("if (\(isSome)) {") printer.indent { - printer.write( - "\(structVar) = \(JSGlueVariableScope.reservedStructHelpers).\(base).lift();" - ) - } - printer.write("} else {") - printer.indent { - printer.write("\(structVar) = \(absenceLiteral);") - } - printer.write("}") - resultExpr = structVar - case .array(let elementType): - let arrayVar = scope.variable("arrayValue") - printer.write("let \(arrayVar);") - printer.write("if (\(isSome)) {") - try printer.indent { - let arrayLiftFragment = try arrayLift(elementType: elementType) - let liftResults = try arrayLiftFragment.printCode([], context) - if let liftResult = liftResults.first { - printer.write("\(arrayVar) = \(liftResult);") + for line in bufferPrinter.lines { + printer.write(line) } + printer.write("\(resultVar) = \(innerExpr);") } printer.write("} else {") printer.indent { - printer.write("\(arrayVar) = \(absenceLiteral);") + printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("}") - resultExpr = arrayVar - case .dictionary(let valueType): - let dictVar = scope.variable("dictValue") - printer.write("let \(dictVar);") - printer.write("if (\(isSome)) {") - try printer.indent { - let dictLiftFragment = try dictionaryLift(valueType: valueType) - let liftResults = try dictLiftFragment.printCode([], context) - if let liftResult = liftResults.first { - printer.write("\(dictVar) = \(liftResult);") - } - } - printer.write("} else {") - printer.indent { - printer.write("\(dictVar) = \(absenceLiteral);") + return [resultVar] + } else { + return ["\(isSome) ? \(innerExpr) : \(absenceLiteral)"] + } + } + ) + } + + static func optionalLowerParameter( + wrappedType: BridgeType, + kind: JSOptionalKind + ) throws -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, desc.wasmParams.count == 1 { + let wasmType = desc.wasmParams[0].type + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + let presenceExpr = kind.presenceCheck(value: value) + printer.write("const \(isSomeVar) = \(presenceExpr);") + let coerced: String + if let coerce = glue.lowerCoerce { + coerced = coerce.replacingOccurrences(of: "$0", with: value) + } else { + coerced = value } - printer.write("}") - resultExpr = dictVar - default: - resultExpr = "\(isSome) ? \(wrappedValue) : \(absenceLiteral)" + return ["+\(isSomeVar)", "\(isSomeVar) ? \(coerced) : \(wasmType.jsZeroLiteral)"] } + ) + } - return [resultExpr] - } + let innerFragment = try lowerParameter(type: wrappedType) + return try compositeOptionalLowerParameter( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment ) } - static func optionalLowerParameter(wrappedType: BridgeType) throws -> IntrinsicJSFragment { + private static func compositeOptionalLowerParameter( + wrappedType: BridgeType, + kind: JSOptionalKind, + innerFragment: IntrinsicJSFragment + ) throws -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + let isStackConvention = desc.optionalConvention == .stackABI + return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) let value = arguments[0] + scope.reserveNames(arguments) let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(value) != null;") + let presenceExpr = kind.presenceCheck(value: value) + printer.write("const \(isSomeVar) = \(presenceExpr);") - switch wrappedType { - case .swiftStruct(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let cleanupVar = scope.variable("\(value)Cleanup") - printer.write("let \(cleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let resultVar = scope.variable("structResult") - printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(base).lower(\(value));" - ) - printer.write("\(cleanupVar) = \(resultVar).cleanup;") + let innerCleanup = CodeFragmentPrinter() + var resultVars: [String] = [] + + if !isStackConvention { + for param in desc.wasmParams { + resultVars.append(scope.variable(param.name)) } - printer.write("}") - cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - printer.write("\(JSGlueVariableScope.reservedI32Stack).push(+\(isSomeVar));") - return [] - case .string, .rawValueEnum(_, .string): - let bytesVar = scope.variable("\(value)Bytes") - let idVar = scope.variable("\(value)Id") + if !resultVars.isEmpty { + printer.write("let \(resultVars.joined(separator: ", "));") + } + } - printer.write("let \(idVar), \(bytesVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));") - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") + let ifBodyPrinter = CodeFragmentPrinter() + let innerResults = try innerFragment.printCode( + [value], + context.with(\.printer, ifBodyPrinter).with(\.cleanupCode, innerCleanup) + ) + + let innerCleanupLines = innerCleanup.lines.filter { + !$0.trimmingCharacters(in: .whitespaces).isEmpty + } + let releasePrefix = "\(JSGlueVariableScope.reservedSwift).memory.release(" + let inlineCleanupResultIndex: Int? = { + guard innerCleanupLines.count == 1 else { return nil } + let cleanupLine = innerCleanupLines[0].trimmingCharacters(in: .whitespacesAndNewlines) + for (index, innerResult) in innerResults.enumerated() where index < resultVars.count { + if cleanupLine == "\(releasePrefix)\(innerResult));" { + return index + } } - printer.write("}") - return ["+\(isSomeVar)", "\(isSomeVar) ? \(idVar) : 0", "\(isSomeVar) ? \(bytesVar).length : 0"] - case .jsValue: - let lowered = try jsValueLower.printCode([value], context) - return ["+\(isSomeVar)"] + lowered - case .associatedValueEnum(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let caseIdVar = scope.variable("\(value)CaseId") - let cleanupVar = scope.variable("\(value)Cleanup") + return nil + }() + let hasCleanup = !innerCleanupLines.isEmpty + let cleanupVar = hasCleanup && inlineCleanupResultIndex == nil ? scope.variable("\(value)Cleanup") : nil - printer.write("let \(caseIdVar), \(cleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let resultVar = scope.variable("enumResult") - printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" - ) - printer.write("\(caseIdVar) = \(resultVar).caseId;") - printer.write("\(cleanupVar) = \(resultVar).cleanup;") + if let cleanupVar { + printer.write("let \(cleanupVar);") + } + printer.write("if (\(isSomeVar)) {") + printer.indent { + for line in ifBodyPrinter.lines { + printer.write(line) } - printer.write("}") - cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - - return ["+\(isSomeVar)", "\(isSomeVar) ? \(caseIdVar) : 0"] - case .rawValueEnum: - // Raw value enums with optional - falls through to handle based on raw type - return ["+\(isSomeVar)", "\(isSomeVar) ? \(value) : 0"] - case .array(let elementType): - let cleanupArrayVar = scope.variable("\(value)Cleanups") - printer.write("const \(cleanupArrayVar) = [];") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let arrayLowerFragment = try arrayLower(elementType: elementType) - let arrayCleanup = CodeFragmentPrinter() - let _ = try arrayLowerFragment.printCode( - [value], - context.with(\.cleanupCode, arrayCleanup) - ) - if !arrayCleanup.lines.isEmpty { - for line in arrayCleanup.lines { - printer.write("\(cleanupArrayVar).push(() => { \(line) });") - } + + if !isStackConvention { + for (resultVar, innerResult) in zip(resultVars, innerResults) { + printer.write("\(resultVar) = \(innerResult);") } } - printer.write("}") - cleanupCode.write("for (const cleanup of \(cleanupArrayVar)) { cleanup(); }") - printer.write("\(JSGlueVariableScope.reservedI32Stack).push(+\(isSomeVar));") - return [] - case .dictionary(let valueType): - let cleanupArrayVar = scope.variable("\(value)Cleanups") - printer.write("const \(cleanupArrayVar) = [];") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let dictLowerFragment = try dictionaryLower(valueType: valueType) - let dictCleanup = CodeFragmentPrinter() - let _ = try dictLowerFragment.printCode( - [value], - context.with(\.cleanupCode, dictCleanup) - ) - if !dictCleanup.lines.isEmpty { - for line in dictCleanup.lines { - printer.write("\(cleanupArrayVar).push(() => { \(line) });") + + if let cleanupVar { + printer.write("\(cleanupVar) = () => {") + printer.indent { + for line in innerCleanupLines { + printer.write(line) } } + printer.write("};") } - printer.write("}") - cleanupCode.write("for (const cleanup of \(cleanupArrayVar)) { cleanup(); }") - printer.write("\(JSGlueVariableScope.reservedI32Stack).push(+\(isSomeVar));") - return [] - default: - switch wrappedType { - case .swiftHeapObject: - return ["+\(isSomeVar)", "\(isSomeVar) ? \(value).pointer : 0"] - case .swiftProtocol: - return [ - "+\(isSomeVar)", - "\(isSomeVar) ? \(JSGlueVariableScope.reservedSwift).memory.retain(\(value)) : 0", - ] - case .jsObject: - let idVar = scope.variable("id") - printer.write("let \(idVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + } + + let hasPlaceholders = !isStackConvention && !desc.wasmParams.isEmpty + if hasPlaceholders { + printer.write("} else {") + printer.indent { + for (resultVar, param) in zip(resultVars, desc.wasmParams) { + printer.write("\(resultVar) = \(param.type.jsZeroLiteral);") } - printer.write("}") - return ["+\(isSomeVar)", "\(isSomeVar) ? \(idVar) : 0"] - default: - return ["+\(isSomeVar)", "\(isSomeVar) ? \(value) : 0"] } } + printer.write("}") + if let inlineCleanupResultIndex { + let retainedVar = resultVars[inlineCleanupResultIndex] + cleanupCode.write("if (\(isSomeVar)) { \(releasePrefix)\(retainedVar)); }") + } else if let cleanupVar { + emitCleanupCall(cleanupVar, into: cleanupCode) + } + + if isStackConvention { + scope.emitPushI32Parameter("+\(isSomeVar)", printer: printer) + return [] + } else { + return ["+\(isSomeVar)"] + resultVars + } } ) } - static func optionalLiftReturn( - wrappedType: BridgeType, - kind: JSOptionalKind - ) -> IntrinsicJSFragment { + private static func optionalLiftReturnFromStorage(storage: String) -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: [], + printCode: { _, context in + let (scope, printer) = (context.scope, context.printer) + let resultVar = scope.variable("optResult") + printer.write("const \(resultVar) = \(storage);") + printer.write("\(storage) = undefined;") + return [resultVar] + } + ) + } + + private static func optionalLiftReturnWithPresenceFlag( + wrappedType: BridgeType, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { let absenceLiteral = kind.absenceLiteral return IntrinsicJSFragment( parameters: [], - printCode: { arguments, context in + printCode: { _, context in let (scope, printer) = (context.scope, context.printer) + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(scope.popI32());") + + let innerFragment = try liftReturn(type: wrappedType) + + let innerPrinter = CodeFragmentPrinter() + let innerResults = try innerFragment.printCode([], context.with(\.printer, innerPrinter)) + let innerExpr = innerResults.first ?? "undefined" + + if innerPrinter.lines.isEmpty { + return ["\(isSomeVar) ? \(innerExpr) : \(absenceLiteral)"] + } + let resultVar = scope.variable("optResult") - switch wrappedType { - case .bool: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") - case .int, .uint: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") - case .float: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") - case .double: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") - case .string: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") - case .jsObject, .swiftProtocol: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") - case .swiftHeapObject(let className): - let pointerVar = scope.variable("pointer") - printer.write( - "const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);" - ) - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;") - let constructExpr = - context.hasDirectAccessToSwiftClass - ? "\(className).__construct(\(pointerVar))" - : "_exports['\(className)'].__construct(\(pointerVar))" + printer.write("let \(resultVar);") + printer.write("if (\(isSomeVar)) {") + printer.indent { + for line in innerPrinter.lines { + printer.write(line) + } + printer.write("\(resultVar) = \(innerExpr);") + } + printer.write("} else {") + printer.indent { + printer.write("\(resultVar) = \(absenceLiteral);") + } + printer.write("}") + return [resultVar] + } + ) + } + + private static func optionalLiftReturnAssociatedEnum( + fullName: String, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { + let base = fullName.components(separatedBy: ".").last ?? fullName + let absenceLiteral = kind.absenceLiteral + return IntrinsicJSFragment( + parameters: [], + printCode: { _, context in + let (scope, printer) = (context.scope, context.printer) + let resultVar = scope.variable("optResult") + let tagVar = scope.variable("tag") + printer.write("const \(tagVar) = \(scope.popI32());") + printer.write( + "const \(resultVar) = \(tagVar) === -1 ? \(absenceLiteral) : \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(tagVar));" + ) + return [resultVar] + } + ) + } + + private static func optionalLiftReturnHeapObject( + className: String, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { + let absenceLiteral = kind.absenceLiteral + return IntrinsicJSFragment( + parameters: [], + printCode: { _, context in + let (scope, printer) = (context.scope, context.printer) + let resultVar = scope.variable("optResult") + let pointerVar = scope.variable("pointer") + printer.write( + "const \(pointerVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject);" + ) + printer.write( + "\(JSGlueVariableScope.reservedStorageToReturnOptionalHeapObject) = undefined;" + ) + let constructExpr = + context.hasDirectAccessToSwiftClass + ? "\(className).__construct(\(pointerVar))" + : "_exports['\(className)'].__construct(\(pointerVar))" + printer.write( + "const \(resultVar) = \(pointerVar) === null ? \(absenceLiteral) : \(constructExpr);" + ) + return [resultVar] + } + ) + } + + private static func optionalLiftReturnStruct( + fullName: String, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { + let base = fullName.components(separatedBy: ".").last ?? fullName + let absenceLiteral = kind.absenceLiteral + return IntrinsicJSFragment( + parameters: [], + printCode: { _, context in + let (scope, printer) = (context.scope, context.printer) + let isSomeVar = scope.variable("isSome") + let resultVar = scope.variable("optResult") + printer.write("const \(isSomeVar) = \(scope.popI32());") + printer.write( + "const \(resultVar) = \(isSomeVar) ? \(JSGlueVariableScope.reservedStructHelpers).\(base).lift() : \(absenceLiteral);" + ) + return [resultVar] + } + ) + } + + static func optionalLiftReturn( + wrappedType: BridgeType, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, let kind = glue.optionalScalarKind { + return optionalLiftReturnFromStorage(storage: kind.storageName) + } + if case .sideChannelReturn(let mode) = desc.optionalConvention, mode != .none { + return optionalLiftReturnFromStorage(storage: JSGlueVariableScope.reservedStorageToReturnString) + } + + if case .swiftHeapObject(let className) = wrappedType { + return optionalLiftReturnHeapObject(className: className, kind: kind) + } + + if case .swiftStruct(let fullName) = wrappedType { + return optionalLiftReturnStruct(fullName: fullName, kind: kind) + } + + if desc.nilSentinel.hasSentinel, case .associatedValueEnum(let fullName) = wrappedType { + return optionalLiftReturnAssociatedEnum(fullName: fullName, kind: kind) + } + + return optionalLiftReturnWithPresenceFlag(wrappedType: wrappedType, kind: kind) + } + + private static func optionalLowerReturnToSideChannel( + mode: OptionalSideChannel, + kind: JSOptionalKind + ) -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + let presenceExpr = kind.presenceCheck(value: value) + printer.write("const \(isSomeVar) = \(presenceExpr);") + + if mode == .storage { printer.write( - "const \(resultVar) = \(pointerVar) === null ? \(absenceLiteral) : \(constructExpr);" + "\(JSGlueVariableScope.reservedStorageToReturnString) = \(isSomeVar) ? \(value) : \(kind.absenceLiteral);" ) - case .caseEnum: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") - case .bool: - printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalBool);" - ) - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalBool) = undefined;") - case .float: - printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalFloat);" - ) - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = undefined;") - case .double: - printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalDouble);" - ) - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = undefined;") - default: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnOptionalInt);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = undefined;") - } - case .associatedValueEnum(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let tagVar = scope.variable("tag") - printer.write("const \(tagVar) = \(scope.popI32());") - let isNullVar = scope.variable("isNull") - printer.write("const \(isNullVar) = (\(tagVar) === -1);") - printer.write("let \(resultVar);") - printer.write("if (\(isNullVar)) {") - printer.indent { - printer.write("\(resultVar) = \(absenceLiteral);") - } - printer.write("} else {") - printer.indent { - printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(tagVar));" - ) - } - printer.write("}") - case .swiftStruct(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(scope.popI32());") - printer.write("let \(resultVar);") + } else { + let idVar = scope.variable("id") + printer.write("let \(idVar) = 0;") printer.write("if (\(isSomeVar)) {") printer.indent { printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(base).lift();" + "\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" ) } - printer.write("} else {") - printer.indent { - printer.write("\(resultVar) = \(absenceLiteral);") - } - printer.write("}") - case .array(let elementType): - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(scope.popI32());") - printer.write("let \(resultVar);") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let arrayLiftFragment = try arrayLift(elementType: elementType) - let liftResults = try arrayLiftFragment.printCode([], context) - if let liftResult = liftResults.first { - printer.write("\(resultVar) = \(liftResult);") - } - } - printer.write("} else {") - printer.indent { - printer.write("\(resultVar) = \(absenceLiteral);") - } printer.write("}") - case .dictionary(let valueType): - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(scope.popI32());") - printer.write("let \(resultVar);") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let dictLiftFragment = try dictionaryLift(valueType: valueType) - let liftResults = try dictLiftFragment.printCode([], context) - if let liftResult = liftResults.first { - printer.write("\(resultVar) = \(liftResult);") - } - } - printer.write("} else {") - printer.indent { - printer.write("\(resultVar) = \(absenceLiteral);") + printer.write("bjs[\"swift_js_return_optional_object\"](\(isSomeVar) ? 1 : 0, \(idVar));") + } + + return [] + } + ) + } + + private static func optionalLowerReturnWithPresenceFlag( + wrappedType: BridgeType, + kind: JSOptionalKind, + innerFragment: IntrinsicJSFragment + ) -> IntrinsicJSFragment { + IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + let presenceExpr = kind.presenceCheck(value: value) + printer.write("const \(isSomeVar) = \(presenceExpr);") + + let innerPrinter = CodeFragmentPrinter() + let innerCleanup = CodeFragmentPrinter() + let innerResults = try innerFragment.printCode( + [value], + context.with(\.printer, innerPrinter).with(\.cleanupCode, innerCleanup) + ) + if !innerResults.isEmpty { + throw BridgeJSLinkError( + message: "Unsupported wrapped type for returning from JS function: \(wrappedType)" + ) + } + + let innerCleanupLines = innerCleanup.lines.filter { + !$0.trimmingCharacters(in: .whitespaces).isEmpty + } + let cleanupVar = innerCleanupLines.isEmpty ? nil : scope.variable("\(value)Cleanup") + if let cleanupVar { + printer.write("let \(cleanupVar);") + } + + printer.write("if (\(isSomeVar)) {") + printer.indent { + for line in innerPrinter.lines { + printer.write(line) } - printer.write("}") - case .jsValue: - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(scope.popI32());") - printer.write("let \(resultVar);") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let lifted = try jsValueLift.printCode([], context) - if let liftedValue = lifted.first { - printer.write("\(resultVar) = \(liftedValue);") + if let cleanupVar { + printer.write("\(cleanupVar) = () => {") + printer.indent { + for line in innerCleanupLines { + printer.write(line) + } } + printer.write("};") } - printer.write("} else {") - printer.indent { - printer.write("\(resultVar) = null;") - } - printer.write("}") - default: - printer.write("const \(resultVar) = \(JSGlueVariableScope.reservedStorageToReturnString);") - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = undefined;") } - return [resultVar] + printer.write("}") + if let cleanupVar { + emitCleanupCall(cleanupVar, into: cleanupCode) + } + + scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) + return [] } ) } @@ -1102,6 +1099,71 @@ struct IntrinsicJSFragment: Sendable { default: break } + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, let scalarKind = glue.optionalScalarKind, + !desc.nilSentinel.hasSentinel, desc.wasmParams.count == 1 + { + let wasmType = desc.wasmParams[0].type + let funcName = scalarKind.funcName + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + let presenceExpr = kind.presenceCheck(value: value) + printer.write("const \(isSomeVar) = \(presenceExpr);") + var coerced: String + if let coerce = glue.effectiveStackLowerCoerce { + coerced = coerce.replacingOccurrences(of: "$0", with: value) + if coerced.contains("?") && !coerced.hasPrefix("(") { + coerced = "(\(coerced))" + } + } else { + coerced = value + } + printer.write( + "bjs[\"\(funcName)\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? \(coerced) : \(wasmType.jsZeroLiteral));" + ) + return [] + } + ) + } + + if case .sideChannelReturn(let mode) = desc.optionalConvention { + if mode == .none { + throw BridgeJSLinkError( + message: "Unsupported wrapped type for returning from JS function: \(wrappedType)" + ) + } + return optionalLowerReturnToSideChannel(mode: mode, kind: kind) + } + + if desc.nilSentinel.hasSentinel { + let innerFragment = try lowerReturn(type: wrappedType, context: .exportSwift) + return sentinelOptionalLowerReturn( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment + ) + } + + let innerFragment = try lowerReturn(type: wrappedType, context: .exportSwift) + return optionalLowerReturnWithPresenceFlag( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment + ) + } + + private static func sentinelOptionalLowerReturn( + wrappedType: BridgeType, + kind: JSOptionalKind, + innerFragment: IntrinsicJSFragment + ) -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + let sentinelLiteral = desc.nilSentinel.jsLiteral + return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in @@ -1111,179 +1173,57 @@ struct IntrinsicJSFragment: Sendable { let presenceExpr = kind.presenceCheck(value: value) printer.write("const \(isSomeVar) = \(presenceExpr);") - switch wrappedType { - case .bool: - printer.write( - "bjs[\"swift_js_return_optional_bool\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? (\(value) ? 1 : 0) : 0);" - ) - case .int, .uint: - printer.write( - "bjs[\"swift_js_return_optional_int\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? (\(value) | 0) : 0);" - ) - case .caseEnum: - printer.write("return \(isSomeVar) ? (\(value) | 0) : -1;") - case .float: - printer.write( - "bjs[\"swift_js_return_optional_float\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? Math.fround(\(value)) : 0.0);" - ) - case .double: - printer.write( - "bjs[\"swift_js_return_optional_double\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? \(value) : 0.0);" - ) - case .string: - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(value);") - } - printer.write("} else {") - printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = null;") - } - printer.write("}") - case .jsObject, .swiftProtocol: - let idVar = scope.variable("id") - printer.write("let \(idVar) = 0;") + let bufferPrinter = CodeFragmentPrinter() + let innerCleanup = CodeFragmentPrinter() + let innerResults = try innerFragment.printCode( + [value], + context.with(\.printer, bufferPrinter).with(\.cleanupCode, innerCleanup) + ) + + let hasSideEffects = !bufferPrinter.lines.isEmpty + let innerExpr = innerResults.first + + let innerCleanupLines = innerCleanup.lines.filter { + !$0.trimmingCharacters(in: .whitespaces).isEmpty + } + if !innerCleanupLines.isEmpty { + let cleanupVar = scope.variable("\(value)Cleanup") + printer.write("let \(cleanupVar);") printer.write("if (\(isSomeVar)) {") printer.indent { - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") - } - printer.write("}") - printer.write("bjs[\"swift_js_return_optional_object\"](\(isSomeVar) ? 1 : 0, \(idVar));") - case .jsValue: - if value != "undefined" { - let lowered = try jsValueLower.printCode([value], context) - let kindVar = lowered[0] - let payload1Var = lowered[1] - let payload2Var = lowered[2] - scope.emitPushI32Parameter(kindVar, printer: printer) - scope.emitPushI32Parameter(payload1Var, printer: printer) - scope.emitPushF64Parameter(payload2Var, printer: printer) - } - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .swiftHeapObject: - printer.write("return \(isSomeVar) ? \(value).pointer : 0;") - case .array(let elementType): - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let arrayLowerFragment = try arrayLower(elementType: elementType) - let arrayCleanup = CodeFragmentPrinter() - let _ = try arrayLowerFragment.printCode( - [value], - context.with(\.cleanupCode, arrayCleanup) - ) - if !arrayCleanup.lines.isEmpty { - for line in arrayCleanup.lines { + for line in bufferPrinter.lines { + printer.write(line) + } + printer.write("\(cleanupVar) = () => {") + printer.indent { + for line in innerCleanupLines { printer.write(line) } } + printer.write("};") } printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(value);") - } - printer.write("} else {") - printer.indent { - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = null;") - } - printer.write("}") - default: - switch rawType { - case .bool: - printer.write( - "bjs[\"swift_js_return_optional_bool\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? (\(value) ? 1 : 0) : 0);" - ) - case .float: - printer.write( - "bjs[\"swift_js_return_optional_float\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? Math.fround(\(value)) : 0.0);" - ) - case .double: - printer.write( - "bjs[\"swift_js_return_optional_double\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? \(value) : 0.0);" - ) - default: - printer.write( - "bjs[\"swift_js_return_optional_int\"](\(isSomeVar) ? 1 : 0, \(isSomeVar) ? (\(value) | 0) : 0);" - ) - } + if let expr = innerExpr { + printer.write("return \(isSomeVar) ? \(expr) : \(sentinelLiteral);") } - case .associatedValueEnum(let fullName): - let base = fullName.components(separatedBy: ".").last ?? fullName - let caseIdVar = scope.variable("caseId") - let cleanupVar = scope.variable("cleanup") + emitCleanupCall(cleanupVar, into: cleanupCode) + } else if hasSideEffects { printer.write("if (\(isSomeVar)) {") printer.indent { - printer.write( - "const { caseId: \(caseIdVar), cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" - ) - printer.write("return \(caseIdVar);") + for line in bufferPrinter.lines { + printer.write(line) + } + if let expr = innerExpr { + printer.write("return \(expr);") + } } printer.write("} else {") printer.indent { - printer.write("return -1;") + printer.write("return \(sentinelLiteral);") } printer.write("}") - cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - case .dictionary(let valueType): - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let cleanupArrayVar = scope.variable("arrayCleanups") - let entriesVar = scope.variable("entries") - let entryVar = scope.variable("entry") - printer.write("const \(cleanupArrayVar) = [];") - printer.write("const \(entriesVar) = Object.entries(\(value));") - printer.write("for (const \(entryVar) of \(entriesVar)) {") - try printer.indent { - let keyVar = scope.variable("key") - let valueVar = scope.variable("value") - printer.write("const [\(keyVar), \(valueVar)] = \(entryVar);") - - let keyFragment = try stackLowerFragment(elementType: .string) - let keyCleanup = CodeFragmentPrinter() - let _ = try keyFragment.printCode( - [keyVar], - context.with(\.cleanupCode, keyCleanup) - ) - if !keyCleanup.lines.isEmpty { - printer.write("\(cleanupArrayVar).push(() => {") - printer.indent { - for line in keyCleanup.lines { - printer.write(line) - } - } - printer.write("});") - } - - let valueFragment = try stackLowerFragment(elementType: valueType) - let valueCleanup = CodeFragmentPrinter() - let _ = try valueFragment.printCode( - [valueVar], - context.with(\.cleanupCode, valueCleanup) - ) - if !valueCleanup.lines.isEmpty { - printer.write("\(cleanupArrayVar).push(() => {") - printer.indent { - for line in valueCleanup.lines { - printer.write(line) - } - } - printer.write("});") - } - } - printer.write("}") - scope.emitPushI32Parameter("\(entriesVar).length", printer: printer) - cleanupCode.write("for (const cleanup of \(cleanupArrayVar)) { cleanup(); }") - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - default: - throw BridgeJSLinkError( - message: "Unsupported wrapped type for returning from JS function: \(wrappedType)" - ) + } else if let expr = innerExpr { + printer.write("return \(isSomeVar) ? \(expr) : \(sentinelLiteral);") } return [] @@ -1294,71 +1234,210 @@ struct IntrinsicJSFragment: Sendable { // MARK: - Protocol Support static func protocolPropertyOptionalToSideChannel(wrappedType: BridgeType) throws -> IntrinsicJSFragment { - switch wrappedType { - case .string, .int, .float, .double, .jsObject, .swiftProtocol: - break - case .rawValueEnum(_, let rawType): - switch rawType { - case .string, .int, .float, .double: - break - default: - throw BridgeJSLinkError( - message: "Unsupported raw value enum type for protocol property side channel: \(rawType)" - ) - } - default: - throw BridgeJSLinkError( - message: "Type \(wrappedType) does not use side channel for protocol property returns" + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, let scalarKind = glue.optionalScalarKind { + let storage = scalarKind.storageName + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + context.printer.write("\(storage) = \(arguments[0]);") + return [] + } ) } - return IntrinsicJSFragment( + if case .sideChannelReturn(let mode) = desc.optionalConvention, + mode != .none + { + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let printer = context.printer + let value = arguments[0] + + printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(value);") + + return [] + } + ) + } + + throw BridgeJSLinkError( + message: "Type \(wrappedType) does not use side channel for protocol property returns" + ) + } + + // MARK: - JS Glue Descriptor Helpers + + private static func popExpression(for wasmType: WasmCoreType, scope: JSGlueVariableScope) -> String { + switch wasmType { + case .i32: return scope.popI32() + case .f32: return scope.popF32() + case .f64: return scope.popF64() + case .pointer: return scope.popPointer() + case .i64: return scope.popI32() + } + } + + private static func emitPush( + for wasmType: WasmCoreType, + value: String, + scope: JSGlueVariableScope, + printer: CodeFragmentPrinter + ) { + switch wasmType { + case .i32: scope.emitPushI32Parameter(value, printer: printer) + case .f32: scope.emitPushF32Parameter(value, printer: printer) + case .f64: scope.emitPushF64Parameter(value, printer: printer) + case .pointer: scope.emitPushPointerParameter(value, printer: printer) + case .i64: scope.emitPushI32Parameter(value, printer: printer) + } + } + + @discardableResult + private static func emitOptionalPlaceholders( + for wrappedType: BridgeType, + scope: JSGlueVariableScope, + printer: CodeFragmentPrinter + ) -> Bool { + let desc = wrappedType.descriptor + let params = desc.wasmParams + if params.isEmpty { + return false + } + for param in params { + emitPush(for: param.type, value: param.type.jsZeroLiteral, scope: scope, printer: printer) + } + return true + } + + private static func stackOptionalLowerWithCleanup( + wrappedType: BridgeType, + kind: JSOptionalKind, + innerFragment: IntrinsicJSFragment + ) -> IntrinsicJSFragment { + IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in - let printer = context.printer + let (scope, printer, cleanup) = (context.scope, context.printer, context.cleanupCode) let value = arguments[0] + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") + + let localCleanup = CodeFragmentPrinter() + let ifBodyPrinter = CodeFragmentPrinter() + try ifBodyPrinter.indent { + let _ = try innerFragment.printCode( + [value], + context.with(\.printer, ifBodyPrinter).with(\.cleanupCode, localCleanup) + ) + } + let cleanupLines = localCleanup.lines.filter { + !$0.trimmingCharacters(in: .whitespaces).isEmpty + } + let innerCleanupVar = cleanupLines.isEmpty ? nil : scope.variable("innerCleanup") + if let innerCleanupVar { + printer.write("let \(innerCleanupVar);") + } + printer.write("if (\(isSomeVar)) {") + for line in ifBodyPrinter.lines { + printer.write(line) + } + if let innerCleanupVar { + printer.indent { + printer.write("\(innerCleanupVar) = () => {") + printer.indent { + for line in cleanupLines { + printer.write(line) + } + } + printer.write("};") + } + } + let placeholderPrinter = CodeFragmentPrinter() + let hasPlaceholders = emitOptionalPlaceholders( + for: wrappedType, + scope: scope, + printer: placeholderPrinter + ) + if hasPlaceholders { + printer.write("} else {") + printer.indent { + for line in placeholderPrinter.lines { + printer.write(line) + } + } + printer.write("}") + } else { + printer.write("}") + } + scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) + if let innerCleanupVar { + emitCleanupCall(innerCleanupVar, into: cleanup) + } + return [] + } + ) + } + + /// Builds lift/lower fragments from a type's scalar coercion info. + /// Returns `nil` when the type has no scalar coercion (complex types) or when + /// the type is nullable/indirect (optionals need dedicated handling). + /// + /// For scalar types, `lowerParameter` is always `.identity` (JS auto-coerces) + /// and `liftReturn`/`liftParameter` share the same lift fragment. + private static func scalarFragments( + for type: BridgeType + ) -> ( + lift: IntrinsicJSFragment, + lower: IntrinsicJSFragment + )? { + if case .nullable = type { return nil } + let desc = type.descriptor + guard let coercion = desc.jsCoercion else { return nil } + + let liftFragment: IntrinsicJSFragment + if let coerce = coercion.liftCoerce { + liftFragment = IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, _ in + return [coerce.replacingOccurrences(of: "$0", with: arguments[0])] + } + ) + } else { + liftFragment = .identity + } - switch wrappedType { - case .string, .rawValueEnum(_, .string): - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(value);") - case .int, .rawValueEnum(_, .int): - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalInt) = \(value);") - case .float, .rawValueEnum(_, .float): - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = \(value);") - case .double, .rawValueEnum(_, .double): - printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalDouble) = \(value);") - case .jsObject, .swiftProtocol: - printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(value);") - default: - fatalError("Unsupported type in protocolPropertyOptionalToSideChannel: \(wrappedType)") + let lowerFragment: IntrinsicJSFragment + if let coerce = coercion.lowerCoerce { + lowerFragment = IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, _ in + return [coerce.replacingOccurrences(of: "$0", with: arguments[0])] } + ) + } else { + lowerFragment = .identity + } - return [] - } - ) + return (lift: liftFragment, lower: lowerFragment) } // MARK: - ExportSwift /// Returns a fragment that lowers a JS value to Wasm core values for parameters static func lowerParameter(type: BridgeType) throws -> IntrinsicJSFragment { + if scalarFragments(for: type) != nil { return .identity } switch type { - case .int, .uint, .float, .double, .bool, .unsafePointer: return .identity case .string: return .stringLowerParameter case .jsObject: return .jsObjectLowerParameter case .jsValue: return .jsValueLower - case .swiftHeapObject: - return .swiftHeapObjectLowerParameter + case .swiftHeapObject: return .swiftHeapObjectLowerParameter case .swiftProtocol: return .jsObjectLowerParameter case .void: return .void - case .nullable(let wrappedType, _): - return try .optionalLowerParameter(wrappedType: wrappedType) - case .caseEnum: return .identity - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: return .stringLowerParameter - default: return .identity - } + case .nullable(let wrappedType, let kind): + return try .optionalLowerParameter(wrappedType: wrappedType, kind: kind) + case .rawValueEnum(_, .string): return .stringLowerParameter case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName return .associatedEnumLowerParameter(enumBase: base) @@ -1384,31 +1463,24 @@ struct IntrinsicJSFragment: Sendable { return try arrayLower(elementType: elementType) case .dictionary(let valueType): return try dictionaryLower(valueType: valueType) + default: + throw BridgeJSLinkError(message: "Unhandled type in lowerParameter: \(type)") } } /// Returns a fragment that lifts a Wasm core value to a JS value for return values static func liftReturn(type: BridgeType) throws -> IntrinsicJSFragment { + if let scalar = scalarFragments(for: type) { return scalar.lift } switch type { - case .int, .float, .double: return .identity - case .uint: return .uintLiftReturn - case .bool: return .boolLiftReturn case .string: return .stringLiftReturn case .jsObject: return .jsObjectLiftReturn case .jsValue: return .jsValueLift case .swiftHeapObject(let name): return .swiftHeapObjectLiftReturn(name) - case .unsafePointer: return .identity case .swiftProtocol: return .jsObjectLiftReturn case .void: return .void case .nullable(let wrappedType, let kind): return .optionalLiftReturn(wrappedType: wrappedType, kind: kind) - case .caseEnum: return .identity - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: return .stringLiftReturn - case .bool: return .boolLiftReturn - default: return .identity - } + case .rawValueEnum(_, .string): return .stringLiftReturn case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName return .associatedEnumLiftReturn(enumBase: base) @@ -1431,6 +1503,8 @@ struct IntrinsicJSFragment: Sendable { return try arrayLift(elementType: elementType) case .dictionary(let valueType): return try dictionaryLift(valueType: valueType) + default: + throw BridgeJSLinkError(message: "Unhandled type in liftReturn: \(type)") } } @@ -1438,14 +1512,11 @@ struct IntrinsicJSFragment: Sendable { /// Returns a fragment that lifts Wasm core values to JS values for parameters static func liftParameter(type: BridgeType, context: BridgeContext = .importTS) throws -> IntrinsicJSFragment { + if let scalar = scalarFragments(for: type) { return scalar.lift } switch type { - case .int, .float, .double: return .identity - case .uint: return .uintLiftParameter - case .bool: return .boolLiftParameter case .string: return .stringLiftParameter case .jsObject: return .jsObjectLiftParameter case .jsValue: return .jsValueLiftParameter - case .unsafePointer: return .identity case .swiftHeapObject(let name): return .swiftHeapObjectLiftParameter(name) case .swiftProtocol: return .jsObjectLiftParameter @@ -1454,14 +1525,8 @@ struct IntrinsicJSFragment: Sendable { message: "Void can't appear in parameters of imported JS functions" ) case .nullable(let wrappedType, let kind): - return try .optionalLiftParameter(wrappedType: wrappedType, kind: kind) - case .caseEnum: return .identity - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: return .stringLiftParameter - case .bool: return .boolLiftParameter - default: return .identity - } + return try .optionalLiftParameter(wrappedType: wrappedType, kind: kind, context: context) + case .rawValueEnum(_, .string): return .stringLiftParameter case .associatedValueEnum(let fullName): switch context { case .importTS: @@ -1519,31 +1584,24 @@ struct IntrinsicJSFragment: Sendable { return try arrayLift(elementType: elementType) case .dictionary(let valueType): return try dictionaryLift(valueType: valueType) + default: + throw BridgeJSLinkError(message: "Unhandled type in liftParameter: \(type)") } } /// Returns a fragment that lowers a JS value to Wasm core values for return values static func lowerReturn(type: BridgeType, context: BridgeContext = .importTS) throws -> IntrinsicJSFragment { + if let scalar = scalarFragments(for: type) { return scalar.lower } switch type { - case .int, .uint, .float, .double: return .identity - case .bool: return .boolLowerReturn case .string: return .stringLowerReturn case .jsObject: return .jsObjectLowerReturn case .jsValue: return .jsValueLowerReturn(context: context) - case .unsafePointer: return .identity - case .swiftHeapObject: - return .swiftHeapObjectLowerReturn + case .swiftHeapObject: return .swiftHeapObjectLowerReturn case .swiftProtocol: return .jsObjectLowerReturn case .void: return .void case .nullable(let wrappedType, let kind): return try .optionalLowerReturn(wrappedType: wrappedType, kind: kind) - case .caseEnum: return .identity - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: return .stringLowerReturn - case .bool: return .boolLowerReturn - default: return .identity - } + case .rawValueEnum(_, .string): return .stringLowerReturn case .associatedValueEnum(let fullName): switch context { case .importTS: @@ -1557,7 +1615,6 @@ struct IntrinsicJSFragment: Sendable { case .swiftStruct(let fullName): switch context { case .importTS: - // ImportTS expects Swift structs to come back as a retained JS object ID. return .jsObjectLowerReturn case .exportSwift: return swiftStructLowerReturn(fullName: fullName) @@ -1584,6 +1641,8 @@ struct IntrinsicJSFragment: Sendable { return try arrayLower(elementType: elementType) case .dictionary(let valueType): return try dictionaryLower(valueType: valueType) + default: + throw BridgeJSLinkError(message: "Unhandled type in lowerReturn: \(type)") } } @@ -1594,14 +1653,12 @@ struct IntrinsicJSFragment: Sendable { return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in - let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) + let (scope, printer) = (context.scope, context.printer) let value = arguments[0] let caseIdVar = scope.variable("caseId") - let cleanupVar = scope.variable("cleanup") printer.write( - "const { caseId: \(caseIdVar), cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" + "const { caseId: \(caseIdVar) } = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" ) - cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") printer.write("return \(caseIdVar);") return [] } @@ -1645,14 +1702,9 @@ struct IntrinsicJSFragment: Sendable { let (scope, printer) = (context.scope, context.printer) let enumName = arguments[0] - printer.write("const __bjs_create\(enumName)Helpers = () => {") - printer.indent() - printer.write( - "return () => ({" - ) + printer.write("const __bjs_create\(enumName)Helpers = () => ({") printer.indent() - // Generate lower function printer.write("lower: (value) => {") try printer.indent { printer.write("const enumTag = value.tag;") @@ -1721,40 +1773,13 @@ struct IntrinsicJSFragment: Sendable { printer.write("}") printer.unindent() printer.write("});") - printer.unindent() - printer.write("};") return [] } ) } - static func simpleEnumHelper(enumDefinition: ExportedEnum) -> IntrinsicJSFragment { - return IntrinsicJSFragment( - parameters: ["enumName"], - printCode: { arguments, context in - let printer = context.printer - let enumName = arguments[0] - printer.write("const \(enumName) = {") - printer.indent { - for (index, enumCase) in enumDefinition.cases.enumerated() { - let caseName = enumCase.name.capitalizedFirstLetter - let value = enumCase.jsValue( - rawType: enumDefinition.rawType, - index: index - ) - printer.write("\(caseName): \(value),") - } - } - printer.write("};") - printer.nextLine() - - return [] - } - ) - } - - static func rawValueEnumHelper(enumDefinition: ExportedEnum) -> IntrinsicJSFragment { + static func caseEnumHelper(enumDefinition: ExportedEnum) -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["enumName"], printCode: { arguments, context in @@ -1869,7 +1894,7 @@ struct IntrinsicJSFragment: Sendable { private static func associatedValuePushPayload(type: BridgeType) throws -> IntrinsicJSFragment { switch type { case .nullable(let wrappedType, let kind): - return associatedValueOptionalPushPayload(wrappedType: wrappedType, kind: kind) + return try associatedValueOptionalPushPayload(wrappedType: wrappedType, kind: kind) default: return try stackLowerFragment(elementType: type) } @@ -1878,183 +1903,43 @@ struct IntrinsicJSFragment: Sendable { private static func associatedValueOptionalPushPayload( wrappedType: BridgeType, kind: JSOptionalKind - ) -> IntrinsicJSFragment { - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer, cleanup) = (context.scope, context.printer, context.cleanupCode) - let value = arguments[0] - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") - - 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));") - scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) - scope.emitPushI32Parameter(idVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .int, .uint: - scope.emitPushI32Parameter("\(isSomeVar) ? (\(value) | 0) : 0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .bool: - scope.emitPushI32Parameter("\(isSomeVar) ? (\(value) ? 1 : 0) : 0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .float: - scope.emitPushF32Parameter("\(isSomeVar) ? Math.fround(\(value)) : 0.0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .double: - scope.emitPushF64Parameter("\(isSomeVar) ? \(value) : 0.0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .caseEnum: - scope.emitPushI32Parameter("\(isSomeVar) ? (\(value) | 0) : 0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .rawValueEnum(_, let rawType): - switch rawType { - 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));" - ) - scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) - scope.emitPushI32Parameter(idVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .float: - scope.emitPushF32Parameter("\(isSomeVar) ? Math.fround(\(value)) : 0.0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .double: - scope.emitPushF64Parameter("\(isSomeVar) ? \(value) : 0.0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - default: - scope.emitPushI32Parameter("\(isSomeVar) ? (\(value) | 0) : 0", printer: printer) - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - } - case .swiftStruct(let structName): - let structBase = structName.components(separatedBy: ".").last ?? structName - let nestedCleanupVar = scope.variable("nestedCleanup") - printer.write("let \(nestedCleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let structResultVar = scope.variable("structResult") - printer.write( - "const \(structResultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(structBase).lower(\(value));" - ) - printer.write("\(nestedCleanupVar) = \(structResultVar).cleanup;") - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - cleanup.write("if (\(nestedCleanupVar)) { \(nestedCleanupVar)(); }") - case .swiftHeapObject: - printer.write("if (\(isSomeVar)) {") - printer.indent { - scope.emitPushPointerParameter("\(value).pointer", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushPointerParameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .jsObject: - let idVar = scope.variable("id") - printer.write("let \(idVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") - scope.emitPushI32Parameter(idVar, printer: printer) - } - printer.write("} else {") - printer.indent { - printer.write("\(idVar) = undefined;") - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - case .associatedValueEnum(let enumName): - let base = enumName.components(separatedBy: ".").last ?? enumName - let caseIdVar = scope.variable("enumCaseId") - let enumCleanupVar = scope.variable("enumCleanup") - printer.write("let \(caseIdVar), \(enumCleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let enumResultVar = scope.variable("enumResult") - printer.write( - "const \(enumResultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" - ) - printer.write("\(caseIdVar) = \(enumResultVar).caseId;") - printer.write("\(enumCleanupVar) = \(enumResultVar).cleanup;") - scope.emitPushI32Parameter(caseIdVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - cleanup.write("if (\(enumCleanupVar)) { \(enumCleanupVar)(); }") - case .array(let elementType): - // Array cleanup references variables declared inside the if block, - // so capture cleanup into a variable declared at the outer scope. - let arrCleanupVar = scope.variable("arrCleanup") - printer.write("let \(arrCleanupVar);") - printer.write("if (\(isSomeVar)) {") - try printer.indent { - let localCleanup = CodeFragmentPrinter() - let arrFragment = try arrayLower(elementType: elementType) - _ = try arrFragment.printCode( - [value], - context.with(\.cleanupCode, localCleanup) - ) - let cleanupLines = localCleanup.lines.filter { - !$0.trimmingCharacters(in: .whitespaces).isEmpty - } - if !cleanupLines.isEmpty { - printer.write("\(arrCleanupVar) = () => {") - printer.indent { - for line in cleanupLines { - printer.write(line) - } - } - printer.write("};") + ) throws -> IntrinsicJSFragment { + let desc = wrappedType.descriptor + if let glue = desc.jsCoercion, desc.wasmParams.count == 1 { + let wasmType = desc.wasmParams[0].type + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") + var coerced: String + if let coerce = glue.effectiveStackLowerCoerce { + coerced = coerce.replacingOccurrences(of: "$0", with: value) + if coerced.contains("?") && !coerced.hasPrefix("(") { + coerced = "(\(coerced))" } + } else { + coerced = value } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - cleanup.write("if (\(arrCleanupVar)) { \(arrCleanupVar)(); }") - default: + emitPush( + for: wasmType, + value: "\(isSomeVar) ? \(coerced) : \(wasmType.jsZeroLiteral)", + scope: scope, + printer: printer + ) scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) + return [] } + ) + } - return [] - } + let innerFragment = try stackLowerFragment(elementType: wrappedType) + return stackOptionalLowerWithCleanup( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment ) } @@ -2112,16 +1997,15 @@ struct IntrinsicJSFragment: Sendable { ) } - static func swiftStructLowerReturn(fullName: String) -> IntrinsicJSFragment { - let base = fullName.components(separatedBy: ".").last ?? fullName - return IntrinsicJSFragment( + private static func swiftStructLower(structBase: String) -> IntrinsicJSFragment { + IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) let value = arguments[0] let cleanupVar = scope.variable("cleanup") printer.write( - "const { cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedStructHelpers).\(base).lower(\(value));" + "const { cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedStructHelpers).\(structBase).lower(\(value));" ) cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") return [] @@ -2129,20 +2013,12 @@ struct IntrinsicJSFragment: Sendable { ) } + static func swiftStructLowerReturn(fullName: String) -> IntrinsicJSFragment { + swiftStructLower(structBase: fullName.components(separatedBy: ".").last ?? fullName) + } + static func swiftStructLowerParameter(structBase: String) -> IntrinsicJSFragment { - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer, cleanupCode) = (context.scope, context.printer, context.cleanupCode) - let value = arguments[0] - let cleanupVar = scope.variable("cleanup") - printer.write( - "const { cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedStructHelpers).\(structBase).lower(\(value));" - ) - cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - return [] - } - ) + swiftStructLower(structBase: structBase) } static func swiftStructLiftReturn(structBase: String) -> IntrinsicJSFragment { @@ -2313,6 +2189,28 @@ struct IntrinsicJSFragment: Sendable { } private static func stackLiftFragment(elementType: BridgeType) throws -> IntrinsicJSFragment { + if case .nullable(let wrappedType, let kind) = elementType { + return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind) + } + let desc = elementType.descriptor + if let glue = desc.jsCoercion, desc.wasmParams.count == 1 { + let wasmType = desc.wasmParams[0].type + return IntrinsicJSFragment( + parameters: [], + printCode: { _, context in + let (scope, printer) = (context.scope, context.printer) + let popExpr = popExpression(for: wasmType, scope: scope) + let varName = scope.variable(glue.varHint) + if let transform = glue.liftCoerce { + let inlined = transform.replacingOccurrences(of: "$0", with: popExpr) + printer.write("const \(varName) = \(inlined);") + } else { + printer.write("const \(varName) = \(popExpr);") + } + return [varName] + } + ) + } switch elementType { case .jsValue: return IntrinsicJSFragment( @@ -2343,44 +2241,14 @@ struct IntrinsicJSFragment: Sendable { return [strVar] } ) - case .bool: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let bVar = scope.variable("bool") - printer.write("const \(bVar) = \(scope.popI32()) !== 0;") - return [bVar] - } - ) - case .int, .uint: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let iVar = scope.variable("int") - printer.write("const \(iVar) = \(scope.popI32());") - return [iVar] - } - ) - case .float: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let fVar = scope.variable("f32") - printer.write("const \(fVar) = \(scope.popF32());") - return [fVar] - } - ) - case .double: + case .rawValueEnum(_, .string): return IntrinsicJSFragment( parameters: [], printCode: { arguments, context in let (scope, printer) = (context.scope, context.printer) - let dVar = scope.variable("f64") - printer.write("const \(dVar) = \(scope.popF64());") - return [dVar] + let varName = scope.variable("rawValue") + printer.write("const \(varName) = \(scope.popString());") + return [varName] } ) case .swiftStruct(let fullName): @@ -2396,59 +2264,6 @@ struct IntrinsicJSFragment: Sendable { return [resultVar] } ) - case .caseEnum: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let varName = scope.variable("caseId") - printer.write("const \(varName) = \(scope.popI32());") - return [varName] - } - ) - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let varName = scope.variable("rawValue") - printer.write("const \(varName) = \(scope.popString());") - return [varName] - } - ) - case .float: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let varName = scope.variable("rawValue") - printer.write("const \(varName) = \(scope.popF32());") - return [varName] - } - ) - case .double: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let varName = scope.variable("rawValue") - printer.write("const \(varName) = \(scope.popF64());") - return [varName] - } - ) - default: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let varName = scope.variable("rawValue") - printer.write("const \(varName) = \(scope.popI32());") - return [varName] - } - ) - } case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName return IntrinsicJSFragment( @@ -2457,7 +2272,7 @@ struct IntrinsicJSFragment: Sendable { let (scope, printer) = (context.scope, context.printer) let resultVar = scope.variable("enumValue") printer.write( - "const \(resultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(scope.popI32()), );" + "const \(resultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(scope.popI32()));" ) return [resultVar] } @@ -2482,33 +2297,45 @@ struct IntrinsicJSFragment: Sendable { let idVar = scope.variable("objId") let objVar = scope.variable("obj") printer.write("const \(idVar) = \(scope.popI32());") - printer.write("const \(objVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(idVar));") + printer.write( + "const \(objVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(idVar));" + ) printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") - return [objVar] - } - ) - case .array(let innerElementType): - return try arrayLift(elementType: innerElementType) - case .dictionary(let valueType): - return try dictionaryLift(valueType: valueType) - case .nullable(let wrappedType, let kind): - return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind) - case .unsafePointer: - return IntrinsicJSFragment( - parameters: [], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let pVar = scope.variable("pointer") - printer.write("const \(pVar) = \(scope.popPointer());") - return [pVar] + return [objVar] } ) - case .void, .closure, .namespaceEnum: + case .array(let innerElementType): + return try arrayLift(elementType: innerElementType) + case .dictionary(let valueType): + return try dictionaryLift(valueType: valueType) + default: throw BridgeJSLinkError(message: "Unsupported array element type: \(elementType)") } } private static func stackLowerFragment(elementType: BridgeType) throws -> IntrinsicJSFragment { + if case .nullable(let wrappedType, let kind) = elementType { + return try optionalElementLowerFragment(wrappedType: wrappedType, kind: kind) + } + let desc = elementType.descriptor + if let glue = desc.jsCoercion, desc.wasmParams.count == 1 { + let wasmType = desc.wasmParams[0].type + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let pushExpr: String + if let coerce = glue.effectiveStackLowerCoerce { + pushExpr = coerce.replacingOccurrences(of: "$0", with: value) + } else { + pushExpr = value + } + emitPush(for: wasmType, value: pushExpr, scope: scope, printer: printer) + return [] + } + ) + } switch elementType { case .jsValue: return IntrinsicJSFragment( @@ -2526,7 +2353,7 @@ struct IntrinsicJSFragment: Sendable { return [] } ) - case .string: + case .string, .rawValueEnum(_, .string): return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in @@ -2534,49 +2361,17 @@ struct IntrinsicJSFragment: Sendable { let value = arguments[0] let bytesVar = scope.variable("bytes") let idVar = scope.variable("id") - printer.write("const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));") - printer.write("const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") + printer.write( + "const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));" + ) + printer.write( + "const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));" + ) scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) scope.emitPushI32Parameter(idVar, printer: printer) return [] } ) - case .bool: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushI32Parameter("\(arguments[0]) ? 1 : 0", printer: printer) - return [] - } - ) - case .int, .uint: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushI32Parameter("(\(arguments[0]) | 0)", printer: printer) - return [] - } - ) - case .float: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushF32Parameter("Math.fround(\(arguments[0]))", printer: printer) - return [] - } - ) - case .double: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushF64Parameter("\(arguments[0])", printer: printer) - return [] - } - ) case .swiftStruct(let fullName): let structBase = fullName.components(separatedBy: ".").last ?? fullName return IntrinsicJSFragment( @@ -2588,68 +2383,11 @@ struct IntrinsicJSFragment: Sendable { printer.write( "const { cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedStructHelpers).\(structBase).lower(\(value));" ) - cleanup.write("if (\(cleanupVar)) { \(cleanupVar)(); }") - return [] - } - ) - case .caseEnum: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushI32Parameter("(\(arguments[0]) | 0)", printer: printer) + emitCleanupCall(cleanupVar, into: cleanup) return [] } ) - case .rawValueEnum(_, let rawType): - switch rawType { - case .string: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let value = arguments[0] - let bytesVar = scope.variable("bytes") - let idVar = scope.variable("id") - printer.write( - "const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));" - ) - printer.write( - "const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));" - ) - scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) - scope.emitPushI32Parameter(idVar, printer: printer) - return [] - } - ) - case .float: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushF32Parameter("Math.fround(\(arguments[0]))", printer: printer) - return [] - } - ) - case .double: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushF64Parameter("\(arguments[0])", printer: printer) - return [] - } - ) - default: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushI32Parameter("(\(arguments[0]) | 0)", printer: printer) - return [] - } - ) - } + case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName return IntrinsicJSFragment( @@ -2663,7 +2401,7 @@ struct IntrinsicJSFragment: Sendable { "const { caseId: \(caseIdVar), cleanup: \(cleanupVar) } = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" ) scope.emitPushI32Parameter(caseIdVar, printer: printer) - cleanup.write("if (\(cleanupVar)) { \(cleanupVar)(); }") + emitCleanupCall(cleanupVar, into: cleanup) return [] } ) @@ -2672,18 +2410,21 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value"], printCode: { arguments, context in let (scope, printer) = (context.scope, context.printer) - scope.emitPushPointerParameter("\(arguments[0]).pointer", printer: printer) + let value = arguments[0] + scope.emitPushPointerParameter("\(value).pointer", printer: printer) return [] } ) - case .jsObject: + case .jsObject, .swiftProtocol: return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in let (scope, printer) = (context.scope, context.printer) let value = arguments[0] let idVar = scope.variable("objId") - printer.write("const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) scope.emitPushI32Parameter(idVar, printer: printer) return [] } @@ -2692,34 +2433,7 @@ struct IntrinsicJSFragment: Sendable { return try arrayLower(elementType: innerElementType) case .dictionary(let valueType): return try dictionaryLower(valueType: valueType) - case .nullable(let wrappedType, let kind): - return try optionalElementLowerFragment( - wrappedType: wrappedType, - kind: kind - ) - case .swiftProtocol: - // Same as jsObject but no cleanup — Swift's AnyProtocol wrapper releases via deinit - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - let value = arguments[0] - let idVar = scope.variable("objId") - printer.write("const \(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") - scope.emitPushI32Parameter(idVar, printer: printer) - return [] - } - ) - case .unsafePointer: - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) - scope.emitPushPointerParameter("(\(arguments[0]) | 0)", printer: printer) - return [] - } - ) - case .void, .closure, .namespaceEnum: + default: throw BridgeJSLinkError(message: "Unsupported array element type for lowering: \(elementType)") } } @@ -2789,27 +2503,23 @@ struct IntrinsicJSFragment: Sendable { printer.write("arrayCleanups.push(() => { \(localCleanupCode) });") } } - printer.write("} else {") - printer.indent { - // Push placeholders so Swift can unconditionally pop value slots - switch wrappedType { - case .float: - scope.emitPushF32Parameter("0.0", printer: printer) - case .double: - scope.emitPushF64Parameter("0.0", printer: printer) - case .swiftStruct: - // No placeholder — Swift only pops struct fields when isSome=1 - break - case .string, .rawValueEnum(_, .string): - scope.emitPushI32Parameter("0", printer: printer) - scope.emitPushI32Parameter("0", printer: printer) - case .swiftHeapObject: - scope.emitPushPointerParameter("0", printer: printer) - default: - scope.emitPushI32Parameter("0", printer: printer) + let placeholderPrinter = CodeFragmentPrinter() + let hasPlaceholders = emitOptionalPlaceholders( + for: wrappedType, + scope: scope, + printer: placeholderPrinter + ) + if hasPlaceholders { + printer.write("} else {") + printer.indent { + for line in placeholderPrinter.lines { + printer.write(line) + } } + printer.write("}") + } else { + printer.write("}") } - printer.write("}") scope.emitPushI32Parameter(isSomeVar, printer: printer) return [] @@ -2817,6 +2527,8 @@ struct IntrinsicJSFragment: Sendable { ) } + // MARK: - Struct Helpers + static func structHelper(structDefinition: ExportedStruct, allStructs: [ExportedStruct]) -> IntrinsicJSFragment { return IntrinsicJSFragment( parameters: ["structName"], @@ -2826,11 +2538,7 @@ struct IntrinsicJSFragment: Sendable { let capturedStructDef = structDefinition let capturedAllStructs = allStructs - printer.write("const __bjs_create\(structName)Helpers = () => {") - printer.indent() - printer.write( - "return () => ({" - ) + printer.write("const __bjs_create\(structName)Helpers = () => ({") printer.indent() printer.write("lower: (value) => {") @@ -2857,18 +2565,12 @@ struct IntrinsicJSFragment: Sendable { printer.write("}") printer.unindent() printer.write("});") - printer.unindent() - printer.write("};") return [] } ) } - private static func findStruct(name: String, structs: [ExportedStruct]) -> ExportedStruct? { - return structs.first(where: { $0.swiftCallName == name || $0.name == name }) - } - private static func generateStructLowerCode( structDef: ExportedStruct, allStructs: [ExportedStruct], @@ -2882,7 +2584,11 @@ struct IntrinsicJSFragment: Sendable { let instanceProps = structDef.properties.filter { !$0.isStatic } for property in instanceProps { - let fragment = try structFieldLowerFragment(field: property, allStructs: allStructs) + let fragment = try structFieldLowerFragment( + type: property.type, + fieldName: property.name, + allStructs: allStructs + ) let fieldValue = "value.\(property.name)" _ = try fragment.printCode( [fieldValue], @@ -2967,7 +2673,7 @@ struct IntrinsicJSFragment: Sendable { } // Cleanup - printer.write("if (\(structCleanupVar)) { \(structCleanupVar)(); }") + emitCleanupCall(structCleanupVar, into: printer) printer.write(contentsOf: methodCleanup) // Lift return value if needed @@ -2990,23 +2696,26 @@ struct IntrinsicJSFragment: Sendable { } private static func structFieldLowerFragment( - field: ExportedProperty, + type: BridgeType, + fieldName: String, allStructs: [ExportedStruct] ) throws -> IntrinsicJSFragment { - switch field.type { + switch type { case .jsValue: preconditionFailure("Struct field of JSValue is not supported yet") case .jsObject: return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, context in - let (scope, printer) = (context.scope, context.printer) + let (scope, printer, cleanup) = (context.scope, context.printer, context.cleanupCode) let value = arguments[0] let idVar = scope.variable("id") printer.write("let \(idVar);") printer.write("if (\(value) != null) {") printer.indent { - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") + printer.write( + "\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" + ) } printer.write("} else {") printer.indent { @@ -3014,273 +2723,60 @@ struct IntrinsicJSFragment: Sendable { } printer.write("}") scope.emitPushI32Parameter("\(idVar) !== undefined ? \(idVar) : 0", printer: printer) + cleanup.write("if(\(idVar) !== undefined && \(idVar) !== 0) {") + cleanup.indent { + cleanup.write("try {") + cleanup.indent { + cleanup.write("\(JSGlueVariableScope.reservedSwift).memory.getObject(\(idVar));") + cleanup.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(idVar));") + } + cleanup.write("} catch(e) { console.warn('BridgeJS: cleanup failed for retained object', e); }") + } + cleanup.write("}") return [idVar] } ) case .nullable(let wrappedType, let kind): - return IntrinsicJSFragment( - parameters: ["value"], - printCode: { arguments, context in - let (scope, printer, cleanup) = (context.scope, context.printer, context.cleanupCode) - let value = arguments[0] - let isSomeVar = scope.variable("isSome") - printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") - - if case .caseEnum = wrappedType { - printer.write("if (\(isSomeVar)) {") - printer.indent { - scope.emitPushI32Parameter("\(value) | 0", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [] - } else if case .rawValueEnum(_, let rawType) = wrappedType { - switch rawType { - case .string: - let idVar = scope.variable("id") - printer.write("let \(idVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let bytesVar = scope.variable("bytes") - printer.write( - "const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));" - ) - printer.write( - "\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));" - ) - scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) - scope.emitPushI32Parameter(idVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [idVar] - case .float: - printer.write("if (\(isSomeVar)) {") - printer.indent { - scope.emitPushF32Parameter("Math.fround(\(value))", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushF32Parameter("0.0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [] - case .double: - printer.write("if (\(isSomeVar)) {") - printer.indent { - scope.emitPushF64Parameter("\(value)", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushF64Parameter("0.0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [] - default: - printer.write("if (\(isSomeVar)) {") - printer.indent { - scope.emitPushI32Parameter("\(value) | 0", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [] - } - } else if case .swiftHeapObject = wrappedType { - let ptrVar = scope.variable("ptr") - printer.write("let \(ptrVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - printer.write("\(ptrVar) = \(value).pointer;") - scope.emitPushPointerParameter("\(ptrVar)", printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushPointerParameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [] - } else if case .swiftStruct(let structName) = wrappedType { - let nestedCleanupVar = scope.variable("nestedCleanup") - printer.write("let \(nestedCleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let structResultVar = scope.variable("structResult") - printer.write( - "const \(structResultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(structName).lower(\(value));" - ) - printer.write("\(nestedCleanupVar) = \(structResultVar).cleanup;") - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - cleanup.write("if (\(nestedCleanupVar)) { \(nestedCleanupVar)(); }") - return [] - } else if case .string = wrappedType { - let idVar = scope.variable("id") - printer.write("let \(idVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let bytesVar = scope.variable("bytes") - printer.write( - "const \(bytesVar) = \(JSGlueVariableScope.reservedTextEncoder).encode(\(value));" - ) - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(bytesVar));") - scope.emitPushI32Parameter("\(bytesVar).length", printer: printer) - scope.emitPushI32Parameter(idVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - scope.emitPushI32Parameter("0", printer: printer) + let wrappedDesc = wrappedType.descriptor + if let glue = wrappedDesc.jsCoercion, wrappedDesc.wasmParams.count == 1 { + let wasmType = wrappedDesc.wasmParams[0].type + return IntrinsicJSFragment( + parameters: ["value"], + printCode: { arguments, context in + let (scope, printer) = (context.scope, context.printer) + let value = arguments[0] + let isSomeVar = scope.variable("isSome") + printer.write("const \(isSomeVar) = \(kind.presenceCheck(value: value));") + let coerced: String + if let coerce = glue.effectiveStackLowerCoerce { + coerced = coerce.replacingOccurrences(of: "$0", with: value) + } else { + coerced = value } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [idVar] - } else if case .jsObject = wrappedType { - let idVar = scope.variable("id") - printer.write("let \(idVar);") printer.write("if (\(isSomeVar)) {") printer.indent { - printer.write("\(idVar) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));") - scope.emitPushI32Parameter(idVar, printer: printer) + emitPush(for: wasmType, value: coerced, scope: scope, printer: printer) } printer.write("} else {") printer.indent { - printer.write("\(idVar) = undefined;") - scope.emitPushI32Parameter("0", printer: printer) + emitPush(for: wasmType, value: wasmType.jsZeroLiteral, scope: scope, printer: printer) } printer.write("}") scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - return [idVar] - } else { - switch wrappedType { - case .int, .uint: - pushOptionalPrimitive( - value: value, - isSomeVar: isSomeVar, - stack: .i32Stack, - convert: "| 0", - zeroValue: "0", - printer: printer, - scope: scope - ) - case .bool: - pushOptionalPrimitive( - value: value, - isSomeVar: isSomeVar, - stack: .i32Stack, - convert: "? 1 : 0", - zeroValue: "0", - printer: printer, - scope: scope - ) - case .float: - pushOptionalPrimitive( - value: value, - isSomeVar: isSomeVar, - stack: .f32Stack, - convert: "Math.fround", - zeroValue: "0.0", - printer: printer, - scope: scope - ) - case .double: - pushOptionalPrimitive( - value: value, - isSomeVar: isSomeVar, - stack: .f64Stack, - convert: nil, - zeroValue: "0.0", - printer: printer, - scope: scope - ) - case .associatedValueEnum(let enumName): - let base = enumName.components(separatedBy: ".").last ?? enumName - let caseIdVar = scope.variable("enumCaseId") - let enumCleanupVar = scope.variable("enumCleanup") - printer.write("let \(caseIdVar), \(enumCleanupVar);") - printer.write("if (\(isSomeVar)) {") - printer.indent { - let enumResultVar = scope.variable("enumResult") - printer.write( - "const \(enumResultVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lower(\(value));" - ) - printer.write("\(caseIdVar) = \(enumResultVar).caseId;") - printer.write("\(enumCleanupVar) = \(enumResultVar).cleanup;") - scope.emitPushI32Parameter(caseIdVar, printer: printer) - } - printer.write("} else {") - printer.indent { - scope.emitPushI32Parameter("0", printer: printer) - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - cleanup.write("if (\(enumCleanupVar)) { \(enumCleanupVar)(); }") - default: - let wrappedFragment = try structFieldLowerFragment( - field: ExportedProperty( - name: field.name, - type: wrappedType, - isReadonly: true, - isStatic: false - ), - allStructs: allStructs - ) - let guardedPrinter = CodeFragmentPrinter() - let guardedCleanup = CodeFragmentPrinter() - _ = try wrappedFragment.printCode( - [value], - context.with(\.printer, guardedPrinter).with(\.cleanupCode, guardedCleanup) - ) - var loweredLines = guardedPrinter.lines - var hoistedCleanupVar: String? - if let first = loweredLines.first { - let trimmed = first.trimmingCharacters(in: .whitespaces) - if trimmed.hasPrefix("const "), - let namePart = trimmed.split(separator: " ").dropFirst().first, - trimmed.contains("= []") - { - hoistedCleanupVar = String(namePart) - loweredLines[0] = "\(hoistedCleanupVar!) = [];" - } - } - if let hoistedName = hoistedCleanupVar { - printer.write("let \(hoistedName);") - } - printer.write("if (\(isSomeVar)) {") - printer.indent { - for line in loweredLines { - printer.write(line) - } - if !guardedCleanup.lines.isEmpty { - cleanup.write("if (\(isSomeVar)) {") - cleanup.indent { - cleanup.write(contentsOf: guardedCleanup) - } - cleanup.write("}") - } - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - } return [] } - } + ) + } + + let innerFragment = try structFieldLowerFragment( + type: wrappedType, + fieldName: fieldName, + allStructs: allStructs + ) + return stackOptionalLowerWithCleanup( + wrappedType: wrappedType, + kind: kind, + innerFragment: innerFragment ) case .swiftStruct(let nestedName): return IntrinsicJSFragment( @@ -3292,7 +2788,7 @@ struct IntrinsicJSFragment: Sendable { printer.write( "const \(structResultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(nestedName).lower(\(value));" ) - context.cleanupCode.write("if (\(structResultVar).cleanup) { \(structResultVar).cleanup(); }") + emitCleanupCall("\(structResultVar).cleanup", into: context.cleanupCode) return [] } ) @@ -3302,61 +2798,17 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value"], printCode: { arguments, context in context.printer.write( - "throw new Error(\"Unsupported struct field type for lowering: \(field.type)\");" + "throw new Error(\"Unsupported struct field type for lowering: \(type)\");" ) return [] } ) default: - return try stackLowerFragment(elementType: field.type) + return try stackLowerFragment(elementType: type) } } /// Helper to push optional primitive values to stack-based parameters - private static func pushOptionalPrimitive( - value: String, - isSomeVar: String, - stack: StackType, - convert: String?, - zeroValue: String, - printer: CodeFragmentPrinter, - scope: JSGlueVariableScope - ) { - let stackName: String - switch stack { - case .i32Stack: stackName = JSGlueVariableScope.reservedI32Stack - case .f32Stack: stackName = JSGlueVariableScope.reservedF32Stack - case .f64Stack: stackName = JSGlueVariableScope.reservedF64Stack - } - - printer.write("if (\(isSomeVar)) {") - printer.indent { - let converted: String - if let convert = convert { - if convert.starts(with: "Math.") { - converted = "\(convert)(\(value))" - } else { - converted = "\(value) \(convert)" - } - } else { - converted = value - } - printer.write("\(stackName).push(\(converted));") - } - printer.write("} else {") - printer.indent { - printer.write("\(stackName).push(\(zeroValue));") - } - printer.write("}") - scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) - } - - private enum StackType { - case i32Stack - case f32Stack - case f64Stack - } - private static func structFieldLiftFragment( field: ExportedProperty, allStructs: [ExportedStruct] @@ -3381,7 +2833,7 @@ struct IntrinsicJSFragment: Sendable { let caseIdVar = scope.variable("enumCaseId") printer.write("const \(caseIdVar) = \(scope.popI32());") printer.write( - "\(optVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(caseIdVar), );" + "\(optVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(caseIdVar));" ) } else { let wrappedFragment = try structFieldLiftFragment( diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index ba25b6ff9..f1f0d3040 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -148,6 +148,28 @@ public enum JSOptionalKind: String, Codable, Equatable, Hashable, Sendable { } } +public enum SwiftEnumRawType: String, CaseIterable, Codable, Sendable { + case string = "String" + case bool = "Bool" + case int = "Int" + case int32 = "Int32" + case int64 = "Int64" + case uint = "UInt" + case uint32 = "UInt32" + case uint64 = "UInt64" + case float = "Float" + case double = "Double" + + public init?(_ rawTypeString: String?) { + guard let rawTypeString = rawTypeString, + let match = Self.allCases.first(where: { $0.rawValue == rawTypeString }) + else { + return nil + } + self = match + } +} + public enum BridgeType: Codable, Equatable, Hashable, Sendable { case int, uint, float, double, string, bool, jsObject(String?), jsValue, swiftHeapObject(String), void case unsafePointer(UnsafePointerType) @@ -165,46 +187,25 @@ public enum BridgeType: Codable, Equatable, Hashable, Sendable { public enum WasmCoreType: String, Codable, Sendable { case i32, i64, f32, f64, pointer -} - -public enum SwiftEnumRawType: String, CaseIterable, Codable, Sendable { - case string = "String" - case bool = "Bool" - case int = "Int" - case int32 = "Int32" - case int64 = "Int64" - case uint = "UInt" - case uint32 = "UInt32" - case uint64 = "UInt64" - case float = "Float" - case double = "Double" - public var wasmCoreType: WasmCoreType? { + public var jsZeroLiteral: String { switch self { - case .string: - return nil - case .bool, .int, .int32, .uint, .uint32: - return .i32 - case .int64, .uint64: - return .i64 - case .float: - return .f32 - case .double: - return .f64 + case .f32, .f64: return "0.0" + case .i32, .i64, .pointer: return "0" } } - public init?(_ rawTypeString: String?) { - guard let rawTypeString = rawTypeString, - let match = Self.allCases.first(where: { $0.rawValue == rawTypeString }) - else { - return nil + public var swiftReturnPlaceholderStmt: String { + switch self { + case .i32: return "return 0" + case .i64: return "return 0" + case .f32: return "return 0.0" + case .f64: return "return 0.0" + case .pointer: return "return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped" } - self = match } } -/// Represents a struct field with name and default value for default parameter values public struct DefaultValueField: Codable, Equatable, Sendable { public let name: String public let value: DefaultValue @@ -222,11 +223,11 @@ public enum DefaultValue: Codable, Equatable, Sendable { case double(Double) case bool(Bool) case null - case enumCase(String, String) // enumName, caseName - case object(String) // className for parameterless constructor - case objectWithArguments(String, [DefaultValue]) // className, constructor argument values - case structLiteral(String, [DefaultValueField]) // structName, field name/value pairs - indirect case array([DefaultValue]) // array literal with element values + case enumCase(String, String) + case object(String) + case objectWithArguments(String, [DefaultValue]) + case structLiteral(String, [DefaultValueField]) + indirect case array([DefaultValue]) } public struct Parameter: Codable, Equatable, Sendable { @@ -247,6 +248,510 @@ public struct Parameter: Codable, Equatable, Sendable { } } +public struct Effects: Codable, Equatable, Sendable { + public var isAsync: Bool + public var isThrows: Bool + public var isStatic: Bool + + public init(isAsync: Bool, isThrows: Bool, isStatic: Bool = false) { + self.isAsync = isAsync + self.isThrows = isThrows + self.isStatic = isStatic + } +} + +public enum StaticContext: Codable, Equatable, Sendable { + case className(String) + case structName(String) + case enumName(String) + case namespaceEnum(String) +} + +// MARK: - ABI Descriptor + +/// How `Optional` is represented at the WASM ABI boundary. +/// +/// The convention is derived from T's descriptor and determines how codegen +/// handles nullable values in both the parameter and return directions. +/// Stack ABI is the general-purpose fallback that works for any T. +public enum OptionalConvention: Sendable, Equatable { + /// Everything goes through the stack (isSome flag + payload pushed/popped). + /// Used for types whose base representation already uses the stack (struct, array, dictionary). + /// This is also the default fallback for unknown types. + case stackABI + + /// isSome is passed as an inline WASM parameter alongside T's normal parameters. + /// For returns, T's return type carries the value (no side channel needed). + /// Used for types with compact representations where the value space has no sentinel + /// (bool, jsValue, closure, caseEnum, associatedValueEnum). + case inlineFlag + + /// Return value goes through a side-channel storage variable; WASM function returns void. + /// For parameters, behaves like `.inlineFlag` (isSome + T's params as direct WASM params). + /// Used for scalar types where Optional return needs disambiguation (int, string, jsObject, etc.). + case sideChannelReturn(OptionalSideChannel) +} + +public enum OptionalSideChannel: Sendable, Equatable { + case none + case storage + case retainedObject +} + +/// A bit pattern that is never a valid value for a type, usable to represent `nil` +/// without an extra `isSome` flag. Inspired by Swift's "extra inhabitant" concept. +/// +/// Types with a nil sentinel can encode Optional in T's own return slot: +/// the sentinel value means absent, any other value means present. +/// Types without a sentinel need either an inline isSome flag or a side channel. +public enum NilSentinel: Sendable, Equatable { + /// No sentinel exists - all bit patterns are valid values. + case none + /// A specific i32 value is never valid (e.g. 0 for object IDs, -1 for enum tags). + case i32(Int32) + /// A null pointer (0) is the sentinel. + case pointer + + public var jsLiteral: String { + switch self { + case .none: fatalError("No sentinel value for .none") + case .i32(let value): return "\(value)" + case .pointer: return "0" + } + } + + public var hasSentinel: Bool { + self != .none + } +} + +/// Identifies which typed optional-return storage slot a scalar type uses. +/// +/// On the Swift -> JS path, Swift calls `swift_js_return_optional_(isSome, value)` +/// which writes to the corresponding `tmpRetOptional` variable in JS. +/// On the JS -> Swift path (protocol returns), the same storage and intrinsic are used +/// unless the type has a nil sentinel (in which case the sentinel path is taken instead). +public enum OptionalScalarKind: String, Sendable { + case bool, int, float, double + + public var storageName: String { "tmpRetOptional\(rawValue.prefix(1).uppercased())\(rawValue.dropFirst())" } + public var funcName: String { "swift_js_return_optional_\(rawValue)" } +} + +/// JS-side coercion info for simple single-value ABI types. +/// Coercion strings use `$0` as a placeholder for the value expression. +/// Grouped within the type descriptor to avoid duplicate per-BridgeType switching. +public struct JSCoercion: Sendable { + public let liftCoerce: String? + public let lowerCoerce: String? + public let stackLowerCoerce: String? + public let varHint: String + public let optionalScalarKind: OptionalScalarKind? + + public init( + liftCoerce: String? = nil, + lowerCoerce: String? = nil, + stackLowerCoerce: String? = nil, + varHint: String, + optionalScalarKind: OptionalScalarKind? = nil + ) { + self.liftCoerce = liftCoerce + self.lowerCoerce = lowerCoerce + self.stackLowerCoerce = stackLowerCoerce + self.varHint = varHint + self.optionalScalarKind = optionalScalarKind + } + + public var effectiveStackLowerCoerce: String? { + stackLowerCoerce ?? lowerCoerce + } +} + +/// Captures the WASM ABI shape for a ``BridgeType`` so codegen can read descriptor fields +/// instead of switching on every concrete type. +/// +/// `wasmReturnType` is for the export direction (Swift->JS), `importReturnType` for import +/// (JS->Swift). They differ for types like `string` and `associatedValueEnum` where the +/// import side returns a pointer/ID while the export side uses the stack. +public struct BridgeTypeDescriptor: Sendable { + public let wasmParams: [(name: String, type: WasmCoreType)] + public let importParams: [(name: String, type: WasmCoreType)] + public let wasmReturnType: WasmCoreType? + public let importReturnType: WasmCoreType? + public let optionalConvention: OptionalConvention + public let nilSentinel: NilSentinel + public let usesStackLifting: Bool + public let accessorTransform: AccessorTransform + public let lowerMethod: LowerMethod + public let jsCoercion: JSCoercion? + + public init( + wasmParams: [(name: String, type: WasmCoreType)], + importParams: [(name: String, type: WasmCoreType)]? = nil, + wasmReturnType: WasmCoreType?, + importReturnType: WasmCoreType? = nil, + optionalConvention: OptionalConvention, + nilSentinel: NilSentinel = .none, + usesStackLifting: Bool = false, + accessorTransform: AccessorTransform, + lowerMethod: LowerMethod, + jsCoercion: JSCoercion? = nil + ) { + self.wasmParams = wasmParams + self.importParams = importParams ?? wasmParams + self.wasmReturnType = wasmReturnType + self.importReturnType = importReturnType ?? wasmReturnType + self.optionalConvention = optionalConvention + self.nilSentinel = nilSentinel + self.usesStackLifting = usesStackLifting + self.accessorTransform = accessorTransform + self.lowerMethod = lowerMethod + self.jsCoercion = jsCoercion + } +} + +/// Describes how to transform a Swift accessor expression before passing it to bridge methods. +/// +/// Most types use `.identity` - the value is passed as-is. Two patterns require transformation: +/// - `@JSClass` types expose their underlying `JSObject` via a `.jsObject` member. +/// - `@JS protocol` wrapper types require a downcast from the existential to the concrete wrapper. +/// +/// Codegen uses this instead of switching on `jsObject(className)` / `swiftProtocol` cases. +public enum AccessorTransform: Sendable, Equatable { + /// Pass the value unchanged (e.g. `ret`). + case identity + /// Access a member on the value (e.g. `ret.jsObject`). Used by `@JSClass` types. + case member(String) + /// Downcast the value (e.g. `(ret as! AnyDrawable)`). Used by `@JS protocol` types. + case cast(String) + + /// Applies the transform to an accessor expression string. + public func apply(_ accessor: String) -> String { + switch self { + case .identity: return accessor + case .member(let member): return "\(accessor).\(member)" + case .cast(let typeName): return "(\(accessor) as! \(typeName))" + } + } + + public var mapClosure: String? { + switch self { + case .identity: return nil + case .member(let member): return "$0.\(member)" + case .cast(let typeName): return "$0 as! \(typeName)" + } + } + + public var flatMapClosure: String? { + switch self { + case .identity: return nil + case .member(let member): return "$0.\(member)" + case .cast(let typeName): return "$0 as? \(typeName)" + } + } + + public func applyToReturnBinding(_ expr: String, isOptional: Bool) -> String { + switch self { + case .cast: + if isOptional { + return "let ret = (\(expr)).flatMap { \(flatMapClosure!) }" + } + return "let ret = \(apply(expr))" + case .identity, .member: + return "let ret = \(expr)" + } + } +} + +/// Which bridge protocol method the codegen calls to lower a value in the export direction. +public enum LowerMethod: Sendable, Equatable { + /// Calls `bridgeJSLowerStackReturn()` - pushes a scalar onto the stack (used within composites like optionals, struct fields). + case stackReturn + /// Calls `bridgeJSLowerReturn()` - serializes the entire value onto the stack (struct, array, dictionary). + case fullReturn + /// Calls `bridgeJSLowerParameter()` and pushes the i32 result. Used by associated value enums (tag + stack payload). + case pushParameter + /// No lowering (void, namespace enums). + case none +} + +extension BridgeType { + /// The ABI descriptor for this type's non-optional representation. + /// + /// This is the single source of truth for how each `BridgeType` maps to WASM ABI. + /// Codegen reads descriptor fields rather than switching on individual types. + /// For `.nullable`, returns the wrapped type's descriptor. + public var descriptor: BridgeTypeDescriptor { + switch self { + case .bool: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .inlineFlag, + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + liftCoerce: "$0 !== 0", + lowerCoerce: "$0 ? 1 : 0", + varHint: "bool", + optionalScalarKind: .bool + ) + ) + case .int: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + stackLowerCoerce: "($0 | 0)", + varHint: "int", + optionalScalarKind: .int + ) + ) + case .uint: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + liftCoerce: "$0 >>> 0", + stackLowerCoerce: "($0 | 0)", + varHint: "int", + optionalScalarKind: .int + ) + ) + case .float: + return BridgeTypeDescriptor( + wasmParams: [("value", .f32)], + wasmReturnType: .f32, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + stackLowerCoerce: "Math.fround($0)", + varHint: "f32", + optionalScalarKind: .float + ) + ) + case .double: + return BridgeTypeDescriptor( + wasmParams: [("value", .f64)], + wasmReturnType: .f64, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + varHint: "f64", + optionalScalarKind: .double + ) + ) + case .string: + return BridgeTypeDescriptor( + wasmParams: [("bytes", .i32), ("length", .i32)], + importParams: [("value", .i32)], + wasmReturnType: nil, + importReturnType: .i32, + optionalConvention: .sideChannelReturn(.storage), + accessorTransform: .identity, + lowerMethod: .stackReturn + ) + case .jsObject(let className): + let transform: AccessorTransform = + if let className, className != "JSObject" { .member("jsObject") } else { .identity } + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .sideChannelReturn(.retainedObject), + nilSentinel: .i32(0), + accessorTransform: transform, + lowerMethod: .stackReturn + ) + case .jsValue: + return BridgeTypeDescriptor( + wasmParams: [("kind", .i32), ("payload1", .i32), ("payload2", .f64)], + wasmReturnType: nil, + optionalConvention: .inlineFlag, + accessorTransform: .identity, + lowerMethod: .stackReturn + ) + case .swiftHeapObject: + return BridgeTypeDescriptor( + wasmParams: [("pointer", .pointer)], + wasmReturnType: .pointer, + optionalConvention: .inlineFlag, + nilSentinel: .pointer, + accessorTransform: .identity, + lowerMethod: .stackReturn + ) + case .unsafePointer: + return BridgeTypeDescriptor( + wasmParams: [("pointer", .pointer)], + wasmReturnType: .pointer, + optionalConvention: .inlineFlag, + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion(stackLowerCoerce: "($0 | 0)", varHint: "pointer") + ) + case .swiftProtocol(let protocolName): + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .sideChannelReturn(.retainedObject), + nilSentinel: .i32(0), + accessorTransform: .cast("Any\(protocolName)"), + lowerMethod: .stackReturn + ) + case .caseEnum: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .inlineFlag, + nilSentinel: .i32(-1), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + stackLowerCoerce: "($0 | 0)", + varHint: "caseId", + optionalScalarKind: .int + ) + ) + case .rawValueEnum(_, let rawType): + switch rawType { + case .string: + return BridgeTypeDescriptor( + wasmParams: [("bytes", .i32), ("length", .i32)], + importParams: [("value", .i32)], + wasmReturnType: nil, + importReturnType: .i32, + optionalConvention: .sideChannelReturn(.storage), + accessorTransform: .identity, + lowerMethod: .stackReturn + ) + case .float: + return BridgeTypeDescriptor( + wasmParams: [("value", .f32)], + wasmReturnType: .f32, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + stackLowerCoerce: "Math.fround($0)", + varHint: "rawValue", + optionalScalarKind: .float + ) + ) + case .double: + return BridgeTypeDescriptor( + wasmParams: [("value", .f64)], + wasmReturnType: .f64, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + varHint: "rawValue", + optionalScalarKind: .double + ) + ) + case .bool: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .inlineFlag, + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + liftCoerce: "$0 !== 0", + lowerCoerce: "$0 ? 1 : 0", + stackLowerCoerce: "$0 ? 1 : 0", + varHint: "rawValue", + optionalScalarKind: .bool + ) + ) + case .int, .int32, .int64, .uint, .uint32, .uint64: + return BridgeTypeDescriptor( + wasmParams: [("value", .i32)], + wasmReturnType: .i32, + optionalConvention: .sideChannelReturn(.none), + accessorTransform: .identity, + lowerMethod: .stackReturn, + jsCoercion: JSCoercion( + stackLowerCoerce: "($0 | 0)", + varHint: "rawValue", + optionalScalarKind: .int + ) + ) + } + case .associatedValueEnum: + return BridgeTypeDescriptor( + wasmParams: [("caseId", .i32)], + wasmReturnType: nil, + importReturnType: .i32, + optionalConvention: .inlineFlag, + nilSentinel: .i32(-1), + usesStackLifting: true, + accessorTransform: .identity, + lowerMethod: .pushParameter + ) + case .closure(_, _): + return BridgeTypeDescriptor( + wasmParams: [("funcRef", .i32)], + wasmReturnType: .i32, + optionalConvention: .inlineFlag, + accessorTransform: .identity, + lowerMethod: .stackReturn + ) + case .swiftStruct: + return BridgeTypeDescriptor( + wasmParams: [], + importParams: [("objectId", .i32)], + wasmReturnType: nil, + importReturnType: .i32, + optionalConvention: .stackABI, + usesStackLifting: true, + accessorTransform: .identity, + lowerMethod: .fullReturn + ) + case .array: + return BridgeTypeDescriptor( + wasmParams: [], + wasmReturnType: nil, + optionalConvention: .stackABI, + usesStackLifting: true, + accessorTransform: .identity, + lowerMethod: .fullReturn + ) + case .dictionary: + return BridgeTypeDescriptor( + wasmParams: [], + wasmReturnType: nil, + optionalConvention: .stackABI, + accessorTransform: .identity, + lowerMethod: .fullReturn + ) + case .void, .namespaceEnum: + return BridgeTypeDescriptor( + wasmParams: [], + wasmReturnType: nil, + optionalConvention: .stackABI, + accessorTransform: .identity, + lowerMethod: .none + ) + case .nullable(let wrapped, _): + return wrapped.descriptor + } + } + + public var needsInlineCollectionHandling: Bool { + if case .nullable = self { return true } + if case .closure = self { return true } + return false + } +} + // MARK: - BridgeType Visitor public protocol BridgeTypeVisitor { @@ -379,39 +884,20 @@ public struct BridgeTypeWalker { } } -public struct Effects: Codable, Equatable, Sendable { - public var isAsync: Bool - public var isThrows: Bool - public var isStatic: Bool +public struct ClosureSignatureCollectorVisitor: BridgeTypeVisitor { + public var signatures: Set = [] - public init(isAsync: Bool, isThrows: Bool, isStatic: Bool = false) { - self.isAsync = isAsync - self.isThrows = isThrows - self.isStatic = isStatic + public init(signatures: Set = []) { + self.signatures = signatures } -} -// MARK: - Static Function Context - -public enum StaticContext: Codable, Equatable, Sendable { - case className(String) - case structName(String) - case enumName(String) - case namespaceEnum(String) + public mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) { + signatures.insert(signature) + } } // MARK: - Struct Skeleton -public struct StructField: Codable, Equatable, Sendable { - public let name: String - public let type: BridgeType - - public init(name: String, type: BridgeType) { - self.name = name - self.type = type - } -} - public struct ExportedStruct: Codable, Equatable, Sendable { public let name: String public let swiftCallName: String @@ -749,15 +1235,6 @@ public struct ExportedSkeleton: Codable { self.exposeToGlobal = exposeToGlobal } - public mutating func append(_ other: ExportedSkeleton) { - self.functions.append(contentsOf: other.functions) - self.classes.append(contentsOf: other.classes) - self.enums.append(contentsOf: other.enums) - self.structs.append(contentsOf: other.structs) - self.protocols.append(contentsOf: other.protocols) - assert(self.exposeToGlobal == other.exposeToGlobal) - } - public var isEmpty: Bool { functions.isEmpty && classes.isEmpty && enums.isEmpty && structs.isEmpty && protocols.isEmpty } @@ -1005,20 +1482,6 @@ public struct ImportedModuleSkeleton: Codable { } } -// MARK: - Closure signature collection visitor - -public struct ClosureSignatureCollectorVisitor: BridgeTypeVisitor { - public var signatures: Set = [] - - public init(signatures: Set = []) { - self.signatures = signatures - } - - public mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) { - signatures.insert(signature) - } -} - // MARK: - Unified Skeleton /// Unified skeleton containing both exported and imported API definitions @@ -1034,7 +1497,7 @@ public struct BridgeJSSkeleton: Codable { } } -// MARK: - BridgeType extension +// MARK: - BridgeType Extension extension BridgeType { /// Maps Swift primitive type names to BridgeType. Returns nil for unknown types. @@ -1069,49 +1532,6 @@ extension BridgeType { } } - public var abiReturnType: WasmCoreType? { - switch self { - case .void: return nil - case .bool: return .i32 - case .int, .uint: return .i32 - case .float: return .f32 - case .double: return .f64 - case .string: return nil - case .jsObject: return .i32 - case .jsValue: return nil - case .swiftHeapObject: - // UnsafeMutableRawPointer is returned as an i32 pointer - return .pointer - case .unsafePointer: - return .pointer - case .nullable: - return nil - case .caseEnum: - return .i32 - case .rawValueEnum(_, let rawType): - return rawType.wasmCoreType - case .associatedValueEnum: - return nil - case .namespaceEnum: - return nil - case .swiftProtocol: - // Protocols pass JSObject IDs as Int32 - return .i32 - case .swiftStruct: - // Structs use stack-based return (no direct WASM return type) - return nil - case .closure: - // Closures pass callback ID as Int32 - return .i32 - case .array: - // Arrays use stack-based return with length prefix (no direct WASM return type) - return nil - case .dictionary: - // Dictionaries use stack-based return with entry count (no direct WASM return type) - return nil - } - } - /// Returns true if this type is optional (nullable with null or undefined). public var isOptional: Bool { if case .nullable = self { return true } @@ -1180,42 +1600,9 @@ extension BridgeType { } } - /// Determines if an optional type requires side-channel communication for protocol property returns - /// - /// 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 .nullable(let wrappedType, _) = self else { - return false - } - - switch wrappedType { - case .string, .int, .float, .double, .jsObject, .swiftProtocol: - return true - case .rawValueEnum(_, let rawType): - switch rawType { - case .string, .int, .float, .double: - return true - default: - return false - } - case .bool, .caseEnum, .swiftHeapObject, .associatedValueEnum: - return false - default: - return false - } - } -} - -extension WasmCoreType { - /// Returns a Swift statement that returns a placeholder value for this Wasm core type. - public var swiftReturnPlaceholderStmt: String { - switch self { - case .i32: return "return 0" - case .i64: return "return 0" - case .f32: return "return 0.0" - case .f64: return "return 0.0" - case .pointer: return "return UnsafeMutableRawPointer(bitPattern: -1).unsafelyUnwrapped" - } + public var usesSideChannelForOptionalReturn: Bool { + guard case .nullable = self else { return false } + if case .sideChannelReturn = descriptor.optionalConvention { return true } + return false } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.swift index 8d697f8ba..1aa97108c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.ReverseOrder.swift @@ -11,9 +11,9 @@ public func _bjs_ClassA_linkedB_get(_ _self: UnsafeMutableRawPointer) -> Void { @_expose(wasm, "bjs_ClassA_linkedB_set") @_cdecl("bjs_ClassA_linkedB_set") -public func _bjs_ClassA_linkedB_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_ClassA_linkedB_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - ClassA.bridgeJSLiftParameter(_self).linkedB = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + ClassA.bridgeJSLiftParameter(_self).linkedB = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.swift index 0a0396075..b523cb4b0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileTypeResolution.swift @@ -47,9 +47,9 @@ public func _bjs_ClassA_linkedB_get(_ _self: UnsafeMutableRawPointer) -> Void { @_expose(wasm, "bjs_ClassA_linkedB_set") @_cdecl("bjs_ClassA_linkedB_set") -public func _bjs_ClassA_linkedB_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_ClassA_linkedB_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - ClassA.bridgeJSLiftParameter(_self).linkedB = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + ClassA.bridgeJSLiftParameter(_self).linkedB = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift index 471d38a6c..afe88e960 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift @@ -1,8 +1,8 @@ @_expose(wasm, "bjs_roundTripOptionalClass") @_cdecl("bjs_roundTripOptionalClass") -public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valuePointer)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -11,9 +11,9 @@ public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valueValue: Unsa @_expose(wasm, "bjs_testOptionalPropertyRoundtrip") @_cdecl("bjs_testOptionalPropertyRoundtrip") -public func _bjs_testOptionalPropertyRoundtrip(_ holderIsSome: Int32, _ holderValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_testOptionalPropertyRoundtrip(_ holderIsSome: Int32, _ holderPointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let ret = testOptionalPropertyRoundtrip(_: Optional.bridgeJSLiftParameter(holderIsSome, holderValue)) + let ret = testOptionalPropertyRoundtrip(_: Optional.bridgeJSLiftParameter(holderIsSome, holderPointer)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -307,9 +307,9 @@ public func _bjs_OptionalPropertyHolder_optionalGreeter_get(_ _self: UnsafeMutab @_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_set") @_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_set") -public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift index 65ff9e67c..9ac72c497 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.swift @@ -544,7 +544,7 @@ public func _bjs_MyViewController_sendHelper(_ _self: UnsafeMutableRawPointer, _ @_cdecl("bjs_MyViewController_delegate_get") public func _bjs_MyViewController_delegate_get(_ _self: UnsafeMutableRawPointer) -> Int32 { #if arch(wasm32) - let ret = MyViewController.bridgeJSLiftParameter(_self).delegate as! AnyMyViewControllerDelegate + let ret = (MyViewController.bridgeJSLiftParameter(_self).delegate as! AnyMyViewControllerDelegate) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift index 925ab5937..a7ef2926d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosure.swift @@ -729,10 +729,10 @@ extension JSTypedClosure where Signature == (Optional) -> Optional Void { +public func _invoke_swift_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Pointer)) return result.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js index 52438d27d..52d761b90 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js @@ -42,20 +42,18 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createPointHelpers = () => { - return () => ({ - lower: (value) => { - f64Stack.push(value.x); - f64Stack.push(value.y); - return { cleanup: undefined }; - }, - lift: () => { - const f64 = f64Stack.pop(); - const f641 = f64Stack.pop(); - return { x: f641, y: f64 }; - } - }); - }; + const __bjs_createPointHelpers = () => ({ + lower: (value) => { + f64Stack.push(value.x); + f64Stack.push(value.y); + return { cleanup: undefined }; + }, + lift: () => { + const f64 = f64Stack.pop(); + const f641 = f64Stack.pop(); + return { x: f641, y: f64 }; + } + }); return { /** @@ -104,8 +102,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -380,7 +377,7 @@ export async function createInstantiator(options, swift) { } } - const PointHelpers = __bjs_createPointHelpers()(); + const PointHelpers = __bjs_createPointHelpers(); structHelpers.Point = PointHelpers; const exports = { @@ -461,7 +458,7 @@ export async function createInstantiator(options, swift) { for (const elem of points) { const { cleanup: structCleanup } = structHelpers.Point.lower(elem); arrayCleanups.push(() => { - if (structCleanup) { structCleanup(); } + structCleanup && structCleanup(); }); } i32Stack.push(points.length); @@ -525,7 +522,7 @@ export async function createInstantiator(options, swift) { for (const elem of points) { const { cleanup: structCleanup } = structHelpers.Point.lower(elem); arrayCleanups.push(() => { - if (structCleanup) { structCleanup(); } + structCleanup && structCleanup(); }); } i32Stack.push(points.length); @@ -653,14 +650,16 @@ export async function createInstantiator(options, swift) { }, processOptionalArray: function bjs_processOptionalArray(values) { const isSome = values != null; - const valuesCleanups = []; + let valuesCleanup; if (isSome) { const arrayCleanups = []; for (const elem of values) { i32Stack.push((elem | 0)); } i32Stack.push(values.length); - valuesCleanups.push(() => { for (const cleanup of arrayCleanups) { cleanup(); } }); + valuesCleanup = () => { + for (const cleanup of arrayCleanups) { cleanup(); } + }; } i32Stack.push(+isSome); instance.exports.bjs_processOptionalArray(); @@ -678,7 +677,7 @@ export async function createInstantiator(options, swift) { } else { optResult = null; } - for (const cleanup of valuesCleanups) { cleanup(); } + valuesCleanup && valuesCleanup(); return optResult; }, processOptionalPointArray: function bjs_processOptionalPointArray(points) { @@ -687,8 +686,7 @@ export async function createInstantiator(options, swift) { const isSome = elem != null ? 1 : 0; if (isSome) { const { cleanup: structCleanup } = structHelpers.Point.lower(elem); - arrayCleanups.push(() => { if (structCleanup) { structCleanup(); } }); - } else { + arrayCleanups.push(() => { structCleanup && structCleanup(); }); } i32Stack.push(isSome); } @@ -841,7 +839,7 @@ export async function createInstantiator(options, swift) { for (const elem1 of elem) { const { cleanup: structCleanup } = structHelpers.Point.lower(elem1); arrayCleanups1.push(() => { - if (structCleanup) { structCleanup(); } + structCleanup && structCleanup(); }); } i32Stack.push(elem.length); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js index bd57f0fd4..09a7a30a1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js index 6182298a1..250e1b1ed 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js @@ -35,50 +35,46 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createConfigHelpers = () => { - return () => ({ - lower: (value) => { - const bytes = textEncoder.encode(value.name); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - i32Stack.push((value.value | 0)); - i32Stack.push(value.enabled ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const bool = i32Stack.pop() !== 0; - const int = i32Stack.pop(); - const string = strStack.pop(); - return { name: string, value: int, enabled: bool }; - } - }); - }; - const __bjs_createMathOperationsHelpers = () => { - return () => ({ - lower: (value) => { - f64Stack.push(value.baseValue); - return { cleanup: undefined }; - }, - lift: () => { - const f64 = f64Stack.pop(); - const instance1 = { baseValue: f64 }; - instance1.add = function(a, b = 10.0) { - const { cleanup: structCleanup } = structHelpers.MathOperations.lower(this); - const ret = instance.exports.bjs_MathOperations_add(a, b); - if (structCleanup) { structCleanup(); } - return ret; - }.bind(instance1); - instance1.multiply = function(a, b) { - const { cleanup: structCleanup } = structHelpers.MathOperations.lower(this); - const ret = instance.exports.bjs_MathOperations_multiply(a, b); - if (structCleanup) { structCleanup(); } - return ret; - }.bind(instance1); - return instance1; - } - }); - }; + const __bjs_createConfigHelpers = () => ({ + lower: (value) => { + const bytes = textEncoder.encode(value.name); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + i32Stack.push((value.value | 0)); + i32Stack.push(value.enabled ? 1 : 0); + return { cleanup: undefined }; + }, + lift: () => { + const bool = i32Stack.pop() !== 0; + const int = i32Stack.pop(); + const string = strStack.pop(); + return { name: string, value: int, enabled: bool }; + } + }); + const __bjs_createMathOperationsHelpers = () => ({ + lower: (value) => { + f64Stack.push(value.baseValue); + return { cleanup: undefined }; + }, + lift: () => { + const f64 = f64Stack.pop(); + const instance1 = { baseValue: f64 }; + instance1.add = function(a, b = 10.0) { + const { cleanup: structCleanup } = structHelpers.MathOperations.lower(this); + const ret = instance.exports.bjs_MathOperations_add(a, b); + structCleanup && structCleanup(); + return ret; + }.bind(instance1); + instance1.multiply = function(a, b) { + const { cleanup: structCleanup } = structHelpers.MathOperations.lower(this); + const ret = instance.exports.bjs_MathOperations_multiply(a, b); + structCleanup && structCleanup(); + return ret; + }.bind(instance1); + return instance1; + } + }); return { /** @@ -126,8 +122,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -356,12 +351,17 @@ export async function createInstantiator(options, swift) { const nameBytes = textEncoder.encode(name); const nameId = swift.memory.retain(nameBytes); const isSome = tag != null; - let tagId, tagBytes; + let bytes, length; if (isSome) { - tagBytes = textEncoder.encode(tag); - tagId = swift.memory.retain(tagBytes); + const tagBytes = textEncoder.encode(tag); + const tagId = swift.memory.retain(tagBytes); + bytes = tagId; + length = tagBytes.length; + } else { + bytes = 0; + length = 0; } - const ret = instance.exports.bjs_ConstructorDefaults_init(nameId, nameBytes.length, count, enabled, status, +isSome, isSome ? tagId : 0, isSome ? tagBytes.length : 0); + const ret = instance.exports.bjs_ConstructorDefaults_init(nameId, nameBytes.length, count, enabled, status, +isSome, bytes, length); return ConstructorDefaults.__construct(ret); } get name() { @@ -404,18 +404,23 @@ export async function createInstantiator(options, swift) { } set tag(value) { const isSome = value != null; - let valueId, valueBytes; + let bytes, length; if (isSome) { - valueBytes = textEncoder.encode(value); - valueId = swift.memory.retain(valueBytes); + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + bytes = valueId; + length = valueBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_ConstructorDefaults_tag_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + instance.exports.bjs_ConstructorDefaults_tag_set(this.pointer, +isSome, bytes, length); } } - const ConfigHelpers = __bjs_createConfigHelpers()(); + const ConfigHelpers = __bjs_createConfigHelpers(); structHelpers.Config = ConfigHelpers; - const MathOperationsHelpers = __bjs_createMathOperationsHelpers()(); + const MathOperationsHelpers = __bjs_createMathOperationsHelpers(); structHelpers.MathOperations = MathOperationsHelpers; const exports = { @@ -448,24 +453,34 @@ export async function createInstantiator(options, swift) { }, testOptionalDefault: function bjs_testOptionalDefault(name = null) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_testOptionalDefault(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_testOptionalDefault(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, testOptionalStringDefault: function bjs_testOptionalStringDefault(greeting = "Hi") { const isSome = greeting != null; - let greetingId, greetingBytes; + let bytes, length; if (isSome) { - greetingBytes = textEncoder.encode(greeting); - greetingId = swift.memory.retain(greetingBytes); + const greetingBytes = textEncoder.encode(greeting); + const greetingId = swift.memory.retain(greetingBytes); + bytes = greetingId; + length = greetingBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_testOptionalStringDefault(+isSome, isSome ? greetingId : 0, isSome ? greetingBytes.length : 0); + instance.exports.bjs_testOptionalStringDefault(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; @@ -494,38 +509,32 @@ export async function createInstantiator(options, swift) { const isSome = point != null; let pointCleanup; if (isSome) { - const structResult = structHelpers.Config.lower(point); - pointCleanup = structResult.cleanup; + const { cleanup: cleanup } = structHelpers.Config.lower(point); + pointCleanup = () => { + if (cleanup) { cleanup(); } + }; } i32Stack.push(+isSome); instance.exports.bjs_testOptionalStructDefault(); const isSome1 = i32Stack.pop(); - let optResult; - if (isSome1) { - optResult = structHelpers.Config.lift(); - } else { - optResult = null; - } - if (pointCleanup) { pointCleanup(); } + const optResult = isSome1 ? structHelpers.Config.lift() : null; + pointCleanup && pointCleanup(); return optResult; }, testOptionalStructWithValueDefault: function bjs_testOptionalStructWithValueDefault(point = { name: "default", value: 42, enabled: true }) { const isSome = point != null; let pointCleanup; if (isSome) { - const structResult = structHelpers.Config.lower(point); - pointCleanup = structResult.cleanup; + const { cleanup: cleanup } = structHelpers.Config.lower(point); + pointCleanup = () => { + if (cleanup) { cleanup(); } + }; } i32Stack.push(+isSome); instance.exports.bjs_testOptionalStructWithValueDefault(); const isSome1 = i32Stack.pop(); - let optResult; - if (isSome1) { - optResult = structHelpers.Config.lift(); - } else { - optResult = null; - } - if (pointCleanup) { pointCleanup(); } + const optResult = isSome1 ? structHelpers.Config.lift() : null; + pointCleanup && pointCleanup(); return optResult; }, testIntArrayDefault: function bjs_testIntArrayDefault(values = [1, 2, 3]) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js index 4f71aa76e..156458317 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -294,7 +293,7 @@ export async function createInstantiator(options, swift) { }, optionalDictionary: function bjs_optionalDictionary(values) { const isSome = values != null; - const valuesCleanups = []; + let valuesCleanup; if (isSome) { const arrayCleanups = []; const entries = Object.entries(values); @@ -310,7 +309,9 @@ export async function createInstantiator(options, swift) { i32Stack.push(id1); } i32Stack.push(entries.length); - valuesCleanups.push(() => { for (const cleanup of arrayCleanups) { cleanup(); } }); + valuesCleanup = () => { + for (const cleanup of arrayCleanups) { cleanup(); } + }; } i32Stack.push(+isSome); instance.exports.bjs_optionalDictionary(); @@ -328,7 +329,7 @@ export async function createInstantiator(options, swift) { } else { optResult = null; } - for (const cleanup of valuesCleanups) { cleanup(); } + valuesCleanup && valuesCleanup(); return optResult; }, nestedDictionary: function bjs_nestedDictionary(values) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js index af81d52fe..cfc2771da 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js @@ -110,567 +110,704 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createPointHelpers = () => { - return () => ({ - lower: (value) => { - f64Stack.push(value.x); - f64Stack.push(value.y); - return { cleanup: undefined }; - }, - lift: () => { - const f64 = f64Stack.pop(); - const f641 = f64Stack.pop(); - return { x: f641, y: f64 }; + const __bjs_createPointHelpers = () => ({ + lower: (value) => { + f64Stack.push(value.x); + f64Stack.push(value.y); + return { cleanup: undefined }; + }, + lift: () => { + const f64 = f64Stack.pop(); + const f641 = f64Stack.pop(); + return { x: f641, y: f64 }; + } + }); + const __bjs_createAPIResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Success, cleanup }; + } + case APIResultValues.Tag.Failure: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Failure, cleanup }; + } + case APIResultValues.Tag.Flag: { + i32Stack.push(value.param0 ? 1 : 0); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Flag, cleanup }; + } + case APIResultValues.Tag.Rate: { + f32Stack.push(Math.fround(value.param0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Rate, cleanup }; + } + case APIResultValues.Tag.Precise: { + f64Stack.push(value.param0); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Precise, cleanup }; + } + case APIResultValues.Tag.Info: { + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Info, cleanup }; + } + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); } - }); - }; - const __bjs_createAPIResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case APIResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Success, cleanup }; - } - case APIResultValues.Tag.Failure: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Failure, cleanup }; - } - case APIResultValues.Tag.Flag: { - i32Stack.push(value.param0 ? 1 : 0); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Flag, cleanup }; - } - case APIResultValues.Tag.Rate: { - f32Stack.push(Math.fround(value.param0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Rate, cleanup }; - } - case APIResultValues.Tag.Precise: { - f64Stack.push(value.param0); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Precise, cleanup }; - } - case APIResultValues.Tag.Info: { - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Info, cleanup }; - } - default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case APIResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: APIResultValues.Tag.Success, param0: string }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case APIResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: APIResultValues.Tag.Success, param0: string }; - } - case APIResultValues.Tag.Failure: { - const int = i32Stack.pop(); - return { tag: APIResultValues.Tag.Failure, param0: int }; - } - case APIResultValues.Tag.Flag: { - const bool = i32Stack.pop() !== 0; - return { tag: APIResultValues.Tag.Flag, param0: bool }; - } - case APIResultValues.Tag.Rate: { - const f32 = f32Stack.pop(); - return { tag: APIResultValues.Tag.Rate, param0: f32 }; - } - case APIResultValues.Tag.Precise: { - const f64 = f64Stack.pop(); - return { tag: APIResultValues.Tag.Precise, param0: f64 }; - } - case APIResultValues.Tag.Info: return { tag: APIResultValues.Tag.Info }; - default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); + case APIResultValues.Tag.Failure: { + const int = i32Stack.pop(); + return { tag: APIResultValues.Tag.Failure, param0: int }; + } + case APIResultValues.Tag.Flag: { + const bool = i32Stack.pop() !== 0; + return { tag: APIResultValues.Tag.Flag, param0: bool }; } + case APIResultValues.Tag.Rate: { + const f32 = f32Stack.pop(); + return { tag: APIResultValues.Tag.Rate, param0: f32 }; + } + case APIResultValues.Tag.Precise: { + const f64 = f64Stack.pop(); + return { tag: APIResultValues.Tag.Precise, param0: f64 }; + } + case APIResultValues.Tag.Info: return { tag: APIResultValues.Tag.Info }; + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); } - }); - }; - const __bjs_createComplexResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case ComplexResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Success, cleanup }; - } - case ComplexResultValues.Tag.Error: { - i32Stack.push((value.param1 | 0)); + } + }); + const __bjs_createComplexResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case ComplexResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Success, cleanup }; + } + case ComplexResultValues.Tag.Error: { + i32Stack.push((value.param1 | 0)); + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Error, cleanup }; + } + case ComplexResultValues.Tag.Status: { + const bytes = textEncoder.encode(value.param2); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + i32Stack.push((value.param1 | 0)); + i32Stack.push(value.param0 ? 1 : 0); + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Status, cleanup }; + } + case ComplexResultValues.Tag.Coordinates: { + f64Stack.push(value.param2); + f64Stack.push(value.param1); + f64Stack.push(value.param0); + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Coordinates, cleanup }; + } + case ComplexResultValues.Tag.Comprehensive: { + const bytes = textEncoder.encode(value.param8); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const bytes1 = textEncoder.encode(value.param7); + const id1 = swift.memory.retain(bytes1); + i32Stack.push(bytes1.length); + i32Stack.push(id1); + const bytes2 = textEncoder.encode(value.param6); + const id2 = swift.memory.retain(bytes2); + i32Stack.push(bytes2.length); + i32Stack.push(id2); + f64Stack.push(value.param5); + f64Stack.push(value.param4); + i32Stack.push((value.param3 | 0)); + i32Stack.push((value.param2 | 0)); + i32Stack.push(value.param1 ? 1 : 0); + i32Stack.push(value.param0 ? 1 : 0); + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Comprehensive, cleanup }; + } + case ComplexResultValues.Tag.Info: { + const cleanup = undefined; + return { caseId: ComplexResultValues.Tag.Info, cleanup }; + } + default: throw new Error("Unknown ComplexResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case ComplexResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: ComplexResultValues.Tag.Success, param0: string }; + } + case ComplexResultValues.Tag.Error: { + const int = i32Stack.pop(); + const string = strStack.pop(); + return { tag: ComplexResultValues.Tag.Error, param0: string, param1: int }; + } + case ComplexResultValues.Tag.Status: { + const string = strStack.pop(); + const int = i32Stack.pop(); + const bool = i32Stack.pop() !== 0; + return { tag: ComplexResultValues.Tag.Status, param0: bool, param1: int, param2: string }; + } + case ComplexResultValues.Tag.Coordinates: { + const f64 = f64Stack.pop(); + const f641 = f64Stack.pop(); + const f642 = f64Stack.pop(); + return { tag: ComplexResultValues.Tag.Coordinates, param0: f642, param1: f641, param2: f64 }; + } + case ComplexResultValues.Tag.Comprehensive: { + const string = strStack.pop(); + const string1 = strStack.pop(); + const string2 = strStack.pop(); + const f64 = f64Stack.pop(); + const f641 = f64Stack.pop(); + const int = i32Stack.pop(); + const int1 = i32Stack.pop(); + const bool = i32Stack.pop() !== 0; + const bool1 = i32Stack.pop() !== 0; + return { tag: ComplexResultValues.Tag.Comprehensive, param0: bool1, param1: bool, param2: int1, param3: int, param4: f641, param5: f64, param6: string2, param7: string1, param8: string }; + } + case ComplexResultValues.Tag.Info: return { tag: ComplexResultValues.Tag.Info }; + default: throw new Error("Unknown ComplexResultValues tag returned from Swift: " + String(tag)); + } + } + }); + const __bjs_createResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case ResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: ResultValues.Tag.Success, cleanup }; + } + case ResultValues.Tag.Failure: { + i32Stack.push((value.param1 | 0)); + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: ResultValues.Tag.Failure, cleanup }; + } + case ResultValues.Tag.Status: { + const bytes = textEncoder.encode(value.param2); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + i32Stack.push((value.param1 | 0)); + i32Stack.push(value.param0 ? 1 : 0); + const cleanup = undefined; + return { caseId: ResultValues.Tag.Status, cleanup }; + } + default: throw new Error("Unknown ResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case ResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: ResultValues.Tag.Success, param0: string }; + } + case ResultValues.Tag.Failure: { + const int = i32Stack.pop(); + const string = strStack.pop(); + return { tag: ResultValues.Tag.Failure, param0: string, param1: int }; + } + case ResultValues.Tag.Status: { + const string = strStack.pop(); + const int = i32Stack.pop(); + const bool = i32Stack.pop() !== 0; + return { tag: ResultValues.Tag.Status, param0: bool, param1: int, param2: string }; + } + default: throw new Error("Unknown ResultValues tag returned from Swift: " + String(tag)); + } + } + }); + const __bjs_createNetworkingResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case NetworkingResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: NetworkingResultValues.Tag.Success, cleanup }; + } + case NetworkingResultValues.Tag.Failure: { + i32Stack.push((value.param1 | 0)); + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: NetworkingResultValues.Tag.Failure, cleanup }; + } + default: throw new Error("Unknown NetworkingResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case NetworkingResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: NetworkingResultValues.Tag.Success, param0: string }; + } + case NetworkingResultValues.Tag.Failure: { + const int = i32Stack.pop(); + const string = strStack.pop(); + return { tag: NetworkingResultValues.Tag.Failure, param0: string, param1: int }; + } + default: throw new Error("Unknown NetworkingResultValues tag returned from Swift: " + String(tag)); + } + } + }); + const __bjs_createAPIOptionalResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIOptionalResultValues.Tag.Success: { + const isSome = value.param0 != null; + if (isSome) { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); i32Stack.push(bytes.length); i32Stack.push(id); - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Error, cleanup }; + } else { + i32Stack.push(0); + i32Stack.push(0); } - case ComplexResultValues.Tag.Status: { + i32Stack.push(isSome ? 1 : 0); + const cleanup = undefined; + return { caseId: APIOptionalResultValues.Tag.Success, cleanup }; + } + case APIOptionalResultValues.Tag.Failure: { + const isSome = value.param1 != null; + i32Stack.push(isSome ? (value.param1 ? 1 : 0) : 0); + i32Stack.push(isSome ? 1 : 0); + const isSome1 = value.param0 != null; + i32Stack.push(isSome1 ? (value.param0 | 0) : 0); + i32Stack.push(isSome1 ? 1 : 0); + const cleanup = undefined; + return { caseId: APIOptionalResultValues.Tag.Failure, cleanup }; + } + case APIOptionalResultValues.Tag.Status: { + const isSome = value.param2 != null; + if (isSome) { const bytes = textEncoder.encode(value.param2); const id = swift.memory.retain(bytes); i32Stack.push(bytes.length); i32Stack.push(id); - i32Stack.push((value.param1 | 0)); - i32Stack.push(value.param0 ? 1 : 0); - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Status, cleanup }; - } - case ComplexResultValues.Tag.Coordinates: { - f64Stack.push(value.param2); - f64Stack.push(value.param1); - f64Stack.push(value.param0); - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Coordinates, cleanup }; - } - case ComplexResultValues.Tag.Comprehensive: { - const bytes = textEncoder.encode(value.param8); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const bytes1 = textEncoder.encode(value.param7); - const id1 = swift.memory.retain(bytes1); - i32Stack.push(bytes1.length); - i32Stack.push(id1); - const bytes2 = textEncoder.encode(value.param6); - const id2 = swift.memory.retain(bytes2); - i32Stack.push(bytes2.length); - i32Stack.push(id2); - f64Stack.push(value.param5); - f64Stack.push(value.param4); - i32Stack.push((value.param3 | 0)); - i32Stack.push((value.param2 | 0)); - i32Stack.push(value.param1 ? 1 : 0); - i32Stack.push(value.param0 ? 1 : 0); - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Comprehensive, cleanup }; - } - case ComplexResultValues.Tag.Info: { - const cleanup = undefined; - return { caseId: ComplexResultValues.Tag.Info, cleanup }; - } - default: throw new Error("Unknown ComplexResultValues tag: " + String(enumTag)); + } else { + i32Stack.push(0); + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + const isSome1 = value.param1 != null; + i32Stack.push(isSome1 ? (value.param1 | 0) : 0); + i32Stack.push(isSome1 ? 1 : 0); + const isSome2 = value.param0 != null; + i32Stack.push(isSome2 ? (value.param0 ? 1 : 0) : 0); + i32Stack.push(isSome2 ? 1 : 0); + const cleanup = undefined; + return { caseId: APIOptionalResultValues.Tag.Status, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case ComplexResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: ComplexResultValues.Tag.Success, param0: string }; - } - case ComplexResultValues.Tag.Error: { - const int = i32Stack.pop(); + default: throw new Error("Unknown APIOptionalResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case APIOptionalResultValues.Tag.Success: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const string = strStack.pop(); - return { tag: ComplexResultValues.Tag.Error, param0: string, param1: int }; + optional = string; + } else { + optional = null; } - case ComplexResultValues.Tag.Status: { - const string = strStack.pop(); - const int = i32Stack.pop(); + return { tag: APIOptionalResultValues.Tag.Success, param0: optional }; + } + case APIOptionalResultValues.Tag.Failure: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const bool = i32Stack.pop() !== 0; - return { tag: ComplexResultValues.Tag.Status, param0: bool, param1: int, param2: string }; - } - case ComplexResultValues.Tag.Coordinates: { - const f64 = f64Stack.pop(); - const f641 = f64Stack.pop(); - const f642 = f64Stack.pop(); - return { tag: ComplexResultValues.Tag.Coordinates, param0: f642, param1: f641, param2: f64 }; + optional = bool; + } else { + optional = null; } - case ComplexResultValues.Tag.Comprehensive: { - const string = strStack.pop(); - const string1 = strStack.pop(); - const string2 = strStack.pop(); - const f64 = f64Stack.pop(); - const f641 = f64Stack.pop(); + const isSome1 = i32Stack.pop(); + let optional1; + if (isSome1) { const int = i32Stack.pop(); - const int1 = i32Stack.pop(); - const bool = i32Stack.pop() !== 0; - const bool1 = i32Stack.pop() !== 0; - return { tag: ComplexResultValues.Tag.Comprehensive, param0: bool1, param1: bool, param2: int1, param3: int, param4: f641, param5: f64, param6: string2, param7: string1, param8: string }; - } - case ComplexResultValues.Tag.Info: return { tag: ComplexResultValues.Tag.Info }; - default: throw new Error("Unknown ComplexResultValues tag returned from Swift: " + String(tag)); - } - } - }); - }; - const __bjs_createResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case ResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: ResultValues.Tag.Success, cleanup }; - } - case ResultValues.Tag.Failure: { - i32Stack.push((value.param1 | 0)); - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: ResultValues.Tag.Failure, cleanup }; - } - case ResultValues.Tag.Status: { - const bytes = textEncoder.encode(value.param2); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - i32Stack.push((value.param1 | 0)); - i32Stack.push(value.param0 ? 1 : 0); - const cleanup = undefined; - return { caseId: ResultValues.Tag.Status, cleanup }; + optional1 = int; + } else { + optional1 = null; } - default: throw new Error("Unknown ResultValues tag: " + String(enumTag)); + return { tag: APIOptionalResultValues.Tag.Failure, param0: optional1, param1: optional }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case ResultValues.Tag.Success: { + case APIOptionalResultValues.Tag.Status: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const string = strStack.pop(); - return { tag: ResultValues.Tag.Success, param0: string }; + optional = string; + } else { + optional = null; } - case ResultValues.Tag.Failure: { + const isSome1 = i32Stack.pop(); + let optional1; + if (isSome1) { const int = i32Stack.pop(); - const string = strStack.pop(); - return { tag: ResultValues.Tag.Failure, param0: string, param1: int }; + optional1 = int; + } else { + optional1 = null; } - case ResultValues.Tag.Status: { - const string = strStack.pop(); - const int = i32Stack.pop(); + const isSome2 = i32Stack.pop(); + let optional2; + if (isSome2) { const bool = i32Stack.pop() !== 0; - return { tag: ResultValues.Tag.Status, param0: bool, param1: int, param2: string }; + optional2 = bool; + } else { + optional2 = null; } - default: throw new Error("Unknown ResultValues tag returned from Swift: " + String(tag)); + return { tag: APIOptionalResultValues.Tag.Status, param0: optional2, param1: optional1, param2: optional }; } + default: throw new Error("Unknown APIOptionalResultValues tag returned from Swift: " + String(tag)); } - }); - }; - const __bjs_createNetworkingResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case NetworkingResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: NetworkingResultValues.Tag.Success, cleanup }; - } - case NetworkingResultValues.Tag.Failure: { - i32Stack.push((value.param1 | 0)); - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: NetworkingResultValues.Tag.Failure, cleanup }; - } - default: throw new Error("Unknown NetworkingResultValues tag: " + String(enumTag)); + } + }); + const __bjs_createTypedPayloadResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case TypedPayloadResultValues.Tag.Precision: { + f32Stack.push(Math.fround(value.param0)); + const cleanup = undefined; + return { caseId: TypedPayloadResultValues.Tag.Precision, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case NetworkingResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: NetworkingResultValues.Tag.Success, param0: string }; - } - case NetworkingResultValues.Tag.Failure: { - const int = i32Stack.pop(); - const string = strStack.pop(); - return { tag: NetworkingResultValues.Tag.Failure, param0: string, param1: int }; - } - default: throw new Error("Unknown NetworkingResultValues tag returned from Swift: " + String(tag)); + case TypedPayloadResultValues.Tag.Direction: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: TypedPayloadResultValues.Tag.Direction, cleanup }; } - } - }); - }; - const __bjs_createAPIOptionalResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case APIOptionalResultValues.Tag.Success: { - const isSome = value.param0 != null; - let id; - if (isSome) { - let bytes = textEncoder.encode(value.param0); - id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - } else { - i32Stack.push(0); - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = undefined; - return { caseId: APIOptionalResultValues.Tag.Success, cleanup }; - } - case APIOptionalResultValues.Tag.Failure: { - const isSome = value.param1 != null; - i32Stack.push(isSome ? (value.param1 ? 1 : 0) : 0); - i32Stack.push(isSome ? 1 : 0); - const isSome1 = value.param0 != null; - i32Stack.push(isSome1 ? (value.param0 | 0) : 0); - i32Stack.push(isSome1 ? 1 : 0); - const cleanup = undefined; - return { caseId: APIOptionalResultValues.Tag.Failure, cleanup }; - } - case APIOptionalResultValues.Tag.Status: { - const isSome = value.param2 != null; - let id; - if (isSome) { - let bytes = textEncoder.encode(value.param2); - id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - } else { - i32Stack.push(0); - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const isSome1 = value.param1 != null; - i32Stack.push(isSome1 ? (value.param1 | 0) : 0); - i32Stack.push(isSome1 ? 1 : 0); - const isSome2 = value.param0 != null; - i32Stack.push(isSome2 ? (value.param0 ? 1 : 0) : 0); - i32Stack.push(isSome2 ? 1 : 0); - const cleanup = undefined; - return { caseId: APIOptionalResultValues.Tag.Status, cleanup }; - } - default: throw new Error("Unknown APIOptionalResultValues tag: " + String(enumTag)); - } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case APIOptionalResultValues.Tag.Success: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const string = strStack.pop(); - optional = string; - } else { - optional = null; - } - return { tag: APIOptionalResultValues.Tag.Success, param0: optional }; - } - case APIOptionalResultValues.Tag.Failure: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const bool = i32Stack.pop() !== 0; - optional = bool; - } else { - optional = null; - } - const isSome1 = i32Stack.pop(); - let optional1; - if (isSome1) { - const int = i32Stack.pop(); - optional1 = int; - } else { - optional1 = null; - } - return { tag: APIOptionalResultValues.Tag.Failure, param0: optional1, param1: optional }; - } - case APIOptionalResultValues.Tag.Status: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const string = strStack.pop(); - optional = string; - } else { - optional = null; - } - const isSome1 = i32Stack.pop(); - let optional1; - if (isSome1) { - const int = i32Stack.pop(); - optional1 = int; - } else { - optional1 = null; - } - const isSome2 = i32Stack.pop(); - let optional2; - if (isSome2) { - const bool = i32Stack.pop() !== 0; - optional2 = bool; - } else { - optional2 = null; - } - return { tag: APIOptionalResultValues.Tag.Status, param0: optional2, param1: optional1, param2: optional }; - } - default: throw new Error("Unknown APIOptionalResultValues tag returned from Swift: " + String(tag)); + case TypedPayloadResultValues.Tag.OptPrecision: { + const isSome = value.param0 != null; + f32Stack.push(isSome ? Math.fround(value.param0) : 0.0); + i32Stack.push(isSome ? 1 : 0); + const cleanup = undefined; + return { caseId: TypedPayloadResultValues.Tag.OptPrecision, cleanup }; + } + case TypedPayloadResultValues.Tag.OptDirection: { + const isSome = value.param0 != null; + i32Stack.push(isSome ? (value.param0 | 0) : 0); + i32Stack.push(isSome ? 1 : 0); + const cleanup = undefined; + return { caseId: TypedPayloadResultValues.Tag.OptDirection, cleanup }; + } + case TypedPayloadResultValues.Tag.Empty: { + const cleanup = undefined; + return { caseId: TypedPayloadResultValues.Tag.Empty, cleanup }; } + default: throw new Error("Unknown TypedPayloadResultValues tag: " + String(enumTag)); } - }); - }; - const __bjs_createTypedPayloadResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case TypedPayloadResultValues.Tag.Precision: { - f32Stack.push(Math.fround(value.param0)); - const cleanup = undefined; - return { caseId: TypedPayloadResultValues.Tag.Precision, cleanup }; - } - case TypedPayloadResultValues.Tag.Direction: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: TypedPayloadResultValues.Tag.Direction, cleanup }; - } - case TypedPayloadResultValues.Tag.OptPrecision: { - const isSome = value.param0 != null; - f32Stack.push(isSome ? Math.fround(value.param0) : 0.0); - i32Stack.push(isSome ? 1 : 0); - const cleanup = undefined; - return { caseId: TypedPayloadResultValues.Tag.OptPrecision, cleanup }; - } - case TypedPayloadResultValues.Tag.OptDirection: { - const isSome = value.param0 != null; - i32Stack.push(isSome ? (value.param0 | 0) : 0); - i32Stack.push(isSome ? 1 : 0); - const cleanup = undefined; - return { caseId: TypedPayloadResultValues.Tag.OptDirection, cleanup }; - } - case TypedPayloadResultValues.Tag.Empty: { - const cleanup = undefined; - return { caseId: TypedPayloadResultValues.Tag.Empty, cleanup }; - } - default: throw new Error("Unknown TypedPayloadResultValues tag: " + String(enumTag)); + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case TypedPayloadResultValues.Tag.Precision: { + const rawValue = f32Stack.pop(); + return { tag: TypedPayloadResultValues.Tag.Precision, param0: rawValue }; + } + case TypedPayloadResultValues.Tag.Direction: { + const caseId = i32Stack.pop(); + return { tag: TypedPayloadResultValues.Tag.Direction, param0: caseId }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case TypedPayloadResultValues.Tag.Precision: { + case TypedPayloadResultValues.Tag.OptPrecision: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const rawValue = f32Stack.pop(); - return { tag: TypedPayloadResultValues.Tag.Precision, param0: rawValue }; + optional = rawValue; + } else { + optional = null; } - case TypedPayloadResultValues.Tag.Direction: { + return { tag: TypedPayloadResultValues.Tag.OptPrecision, param0: optional }; + } + case TypedPayloadResultValues.Tag.OptDirection: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const caseId = i32Stack.pop(); - return { tag: TypedPayloadResultValues.Tag.Direction, param0: caseId }; - } - case TypedPayloadResultValues.Tag.OptPrecision: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const rawValue = f32Stack.pop(); - optional = rawValue; - } else { - optional = null; - } - return { tag: TypedPayloadResultValues.Tag.OptPrecision, param0: optional }; + optional = caseId; + } else { + optional = null; } - case TypedPayloadResultValues.Tag.OptDirection: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const caseId = i32Stack.pop(); - optional = caseId; - } else { - optional = null; - } - return { tag: TypedPayloadResultValues.Tag.OptDirection, param0: optional }; + return { tag: TypedPayloadResultValues.Tag.OptDirection, param0: optional }; + } + case TypedPayloadResultValues.Tag.Empty: return { tag: TypedPayloadResultValues.Tag.Empty }; + default: throw new Error("Unknown TypedPayloadResultValues tag returned from Swift: " + String(tag)); + } + } + }); + const __bjs_createAllTypesResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case AllTypesResultValues.Tag.StructPayload: { + const { cleanup: structCleanup } = structHelpers.Point.lower(value.param0); + const cleanup = () => { + structCleanup && structCleanup(); + }; + return { caseId: AllTypesResultValues.Tag.StructPayload, cleanup }; + } + case AllTypesResultValues.Tag.ClassPayload: { + ptrStack.push(value.param0.pointer); + const cleanup = undefined; + return { caseId: AllTypesResultValues.Tag.ClassPayload, cleanup }; + } + case AllTypesResultValues.Tag.JsObjectPayload: { + const objId = swift.memory.retain(value.param0); + i32Stack.push(objId); + const cleanup = undefined; + return { caseId: AllTypesResultValues.Tag.JsObjectPayload, cleanup }; + } + case AllTypesResultValues.Tag.NestedEnum: { + const { caseId: caseId, cleanup: enumCleanup } = enumHelpers.APIResult.lower(value.param0); + i32Stack.push(caseId); + const cleanup = () => { + enumCleanup && enumCleanup(); + }; + return { caseId: AllTypesResultValues.Tag.NestedEnum, cleanup }; + } + case AllTypesResultValues.Tag.ArrayPayload: { + const arrayCleanups = []; + for (const elem of value.param0) { + i32Stack.push((elem | 0)); + } + i32Stack.push(value.param0.length); + const cleanup = () => { + for (const cleanup of arrayCleanups) { cleanup(); } + }; + return { caseId: AllTypesResultValues.Tag.ArrayPayload, cleanup }; + } + case AllTypesResultValues.Tag.Empty: { + const cleanup = undefined; + return { caseId: AllTypesResultValues.Tag.Empty, cleanup }; + } + default: throw new Error("Unknown AllTypesResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case AllTypesResultValues.Tag.StructPayload: { + const struct = structHelpers.Point.lift(); + return { tag: AllTypesResultValues.Tag.StructPayload, param0: struct }; + } + case AllTypesResultValues.Tag.ClassPayload: { + const ptr = ptrStack.pop(); + const obj = _exports['User'].__construct(ptr); + return { tag: AllTypesResultValues.Tag.ClassPayload, param0: obj }; + } + case AllTypesResultValues.Tag.JsObjectPayload: { + const objId = i32Stack.pop(); + const obj = swift.memory.getObject(objId); + swift.memory.release(objId); + return { tag: AllTypesResultValues.Tag.JsObjectPayload, param0: obj }; + } + case AllTypesResultValues.Tag.NestedEnum: { + const enumValue = enumHelpers.APIResult.lift(i32Stack.pop()); + return { tag: AllTypesResultValues.Tag.NestedEnum, param0: enumValue }; + } + case AllTypesResultValues.Tag.ArrayPayload: { + const arrayLen = i32Stack.pop(); + const arrayResult = []; + for (let i = 0; i < arrayLen; i++) { + const int = i32Stack.pop(); + arrayResult.push(int); } - case TypedPayloadResultValues.Tag.Empty: return { tag: TypedPayloadResultValues.Tag.Empty }; - default: throw new Error("Unknown TypedPayloadResultValues tag returned from Swift: " + String(tag)); + arrayResult.reverse(); + return { tag: AllTypesResultValues.Tag.ArrayPayload, param0: arrayResult }; } + case AllTypesResultValues.Tag.Empty: return { tag: AllTypesResultValues.Tag.Empty }; + default: throw new Error("Unknown AllTypesResultValues tag returned from Swift: " + String(tag)); } - }); - }; - const __bjs_createAllTypesResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case AllTypesResultValues.Tag.StructPayload: { + } + }); + const __bjs_createOptionalAllTypesResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case OptionalAllTypesResultValues.Tag.OptStruct: { + const isSome = value.param0 != null; + let innerCleanup; + if (isSome) { const { cleanup: structCleanup } = structHelpers.Point.lower(value.param0); - const cleanup = () => { - if (structCleanup) { structCleanup(); } + innerCleanup = () => { + structCleanup && structCleanup(); }; - return { caseId: AllTypesResultValues.Tag.StructPayload, cleanup }; } - case AllTypesResultValues.Tag.ClassPayload: { + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + innerCleanup && innerCleanup(); + }; + return { caseId: OptionalAllTypesResultValues.Tag.OptStruct, cleanup }; + } + case OptionalAllTypesResultValues.Tag.OptClass: { + const isSome = value.param0 != null; + if (isSome) { ptrStack.push(value.param0.pointer); - const cleanup = undefined; - return { caseId: AllTypesResultValues.Tag.ClassPayload, cleanup }; + } else { + ptrStack.push(0); } - case AllTypesResultValues.Tag.JsObjectPayload: { + i32Stack.push(isSome ? 1 : 0); + const cleanup = undefined; + return { caseId: OptionalAllTypesResultValues.Tag.OptClass, cleanup }; + } + case OptionalAllTypesResultValues.Tag.OptJSObject: { + const isSome = value.param0 != null; + if (isSome) { const objId = swift.memory.retain(value.param0); i32Stack.push(objId); - const cleanup = undefined; - return { caseId: AllTypesResultValues.Tag.JsObjectPayload, cleanup }; + } else { + i32Stack.push(0); } - case AllTypesResultValues.Tag.NestedEnum: { + i32Stack.push(isSome ? 1 : 0); + const cleanup = undefined; + return { caseId: OptionalAllTypesResultValues.Tag.OptJSObject, cleanup }; + } + case OptionalAllTypesResultValues.Tag.OptNestedEnum: { + const isSome = value.param0 != null; + let innerCleanup; + if (isSome) { const { caseId: caseId, cleanup: enumCleanup } = enumHelpers.APIResult.lower(value.param0); i32Stack.push(caseId); - const cleanup = () => { - if (enumCleanup) { enumCleanup(); } + innerCleanup = () => { + enumCleanup && enumCleanup(); }; - return { caseId: AllTypesResultValues.Tag.NestedEnum, cleanup }; + } else { + i32Stack.push(0); } - case AllTypesResultValues.Tag.ArrayPayload: { + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + innerCleanup && innerCleanup(); + }; + return { caseId: OptionalAllTypesResultValues.Tag.OptNestedEnum, cleanup }; + } + case OptionalAllTypesResultValues.Tag.OptArray: { + const isSome = value.param0 != null; + let innerCleanup; + if (isSome) { const arrayCleanups = []; for (const elem of value.param0) { i32Stack.push((elem | 0)); } i32Stack.push(value.param0.length); - const cleanup = () => { + innerCleanup = () => { for (const cleanup of arrayCleanups) { cleanup(); } }; - return { caseId: AllTypesResultValues.Tag.ArrayPayload, cleanup }; } - case AllTypesResultValues.Tag.Empty: { - const cleanup = undefined; - return { caseId: AllTypesResultValues.Tag.Empty, cleanup }; - } - default: throw new Error("Unknown AllTypesResultValues tag: " + String(enumTag)); + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + innerCleanup && innerCleanup(); + }; + return { caseId: OptionalAllTypesResultValues.Tag.OptArray, cleanup }; + } + case OptionalAllTypesResultValues.Tag.Empty: { + const cleanup = undefined; + return { caseId: OptionalAllTypesResultValues.Tag.Empty, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case AllTypesResultValues.Tag.StructPayload: { + default: throw new Error("Unknown OptionalAllTypesResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case OptionalAllTypesResultValues.Tag.OptStruct: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const struct = structHelpers.Point.lift(); - return { tag: AllTypesResultValues.Tag.StructPayload, param0: struct }; + optional = struct; + } else { + optional = null; } - case AllTypesResultValues.Tag.ClassPayload: { + return { tag: OptionalAllTypesResultValues.Tag.OptStruct, param0: optional }; + } + case OptionalAllTypesResultValues.Tag.OptClass: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const ptr = ptrStack.pop(); const obj = _exports['User'].__construct(ptr); - return { tag: AllTypesResultValues.Tag.ClassPayload, param0: obj }; + optional = obj; + } else { + optional = null; } - case AllTypesResultValues.Tag.JsObjectPayload: { + return { tag: OptionalAllTypesResultValues.Tag.OptClass, param0: optional }; + } + case OptionalAllTypesResultValues.Tag.OptJSObject: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const objId = i32Stack.pop(); const obj = swift.memory.getObject(objId); swift.memory.release(objId); - return { tag: AllTypesResultValues.Tag.JsObjectPayload, param0: obj }; + optional = obj; + } else { + optional = null; } - case AllTypesResultValues.Tag.NestedEnum: { - const enumValue = enumHelpers.APIResult.lift(i32Stack.pop(), ); - return { tag: AllTypesResultValues.Tag.NestedEnum, param0: enumValue }; + return { tag: OptionalAllTypesResultValues.Tag.OptJSObject, param0: optional }; + } + case OptionalAllTypesResultValues.Tag.OptNestedEnum: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const caseId = i32Stack.pop(); + optional = enumHelpers.APIResult.lift(caseId); + } else { + optional = null; } - case AllTypesResultValues.Tag.ArrayPayload: { + return { tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, param0: optional }; + } + case OptionalAllTypesResultValues.Tag.OptArray: { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const arrayLen = i32Stack.pop(); const arrayResult = []; for (let i = 0; i < arrayLen; i++) { @@ -678,173 +815,17 @@ export async function createInstantiator(options, swift) { arrayResult.push(int); } arrayResult.reverse(); - return { tag: AllTypesResultValues.Tag.ArrayPayload, param0: arrayResult }; - } - case AllTypesResultValues.Tag.Empty: return { tag: AllTypesResultValues.Tag.Empty }; - default: throw new Error("Unknown AllTypesResultValues tag returned from Swift: " + String(tag)); - } - } - }); - }; - const __bjs_createOptionalAllTypesResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case OptionalAllTypesResultValues.Tag.OptStruct: { - const isSome = value.param0 != null; - let nestedCleanup; - if (isSome) { - const structResult = structHelpers.Point.lower(value.param0); - nestedCleanup = structResult.cleanup; - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = () => { - if (nestedCleanup) { nestedCleanup(); } - }; - return { caseId: OptionalAllTypesResultValues.Tag.OptStruct, cleanup }; - } - case OptionalAllTypesResultValues.Tag.OptClass: { - const isSome = value.param0 != null; - if (isSome) { - ptrStack.push(value.param0.pointer); - } else { - ptrStack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = undefined; - return { caseId: OptionalAllTypesResultValues.Tag.OptClass, cleanup }; - } - case OptionalAllTypesResultValues.Tag.OptJSObject: { - const isSome = value.param0 != null; - let id; - if (isSome) { - id = swift.memory.retain(value.param0); - i32Stack.push(id); - } else { - id = undefined; - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = undefined; - return { caseId: OptionalAllTypesResultValues.Tag.OptJSObject, cleanup }; - } - case OptionalAllTypesResultValues.Tag.OptNestedEnum: { - const isSome = value.param0 != null; - let enumCaseId, enumCleanup; - if (isSome) { - const enumResult = enumHelpers.APIResult.lower(value.param0); - enumCaseId = enumResult.caseId; - enumCleanup = enumResult.cleanup; - i32Stack.push(enumCaseId); - } else { - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = () => { - if (enumCleanup) { enumCleanup(); } - }; - return { caseId: OptionalAllTypesResultValues.Tag.OptNestedEnum, cleanup }; - } - case OptionalAllTypesResultValues.Tag.OptArray: { - const isSome = value.param0 != null; - let arrCleanup; - if (isSome) { - const arrayCleanups = []; - for (const elem of value.param0) { - i32Stack.push((elem | 0)); - } - i32Stack.push(value.param0.length); - arrCleanup = () => { - for (const cleanup of arrayCleanups) { cleanup(); } - }; - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = () => { - if (arrCleanup) { arrCleanup(); } - }; - return { caseId: OptionalAllTypesResultValues.Tag.OptArray, cleanup }; - } - case OptionalAllTypesResultValues.Tag.Empty: { - const cleanup = undefined; - return { caseId: OptionalAllTypesResultValues.Tag.Empty, cleanup }; - } - default: throw new Error("Unknown OptionalAllTypesResultValues tag: " + String(enumTag)); - } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case OptionalAllTypesResultValues.Tag.OptStruct: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const struct = structHelpers.Point.lift(); - optional = struct; - } else { - optional = null; - } - return { tag: OptionalAllTypesResultValues.Tag.OptStruct, param0: optional }; - } - case OptionalAllTypesResultValues.Tag.OptClass: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const ptr = ptrStack.pop(); - const obj = _exports['User'].__construct(ptr); - optional = obj; - } else { - optional = null; - } - return { tag: OptionalAllTypesResultValues.Tag.OptClass, param0: optional }; - } - case OptionalAllTypesResultValues.Tag.OptJSObject: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const objId = i32Stack.pop(); - const obj = swift.memory.getObject(objId); - swift.memory.release(objId); - optional = obj; - } else { - optional = null; - } - return { tag: OptionalAllTypesResultValues.Tag.OptJSObject, param0: optional }; - } - case OptionalAllTypesResultValues.Tag.OptNestedEnum: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const caseId = i32Stack.pop(); - optional = enumHelpers.APIResult.lift(caseId); - } else { - optional = null; - } - return { tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, param0: optional }; - } - case OptionalAllTypesResultValues.Tag.OptArray: { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const arrayLen = i32Stack.pop(); - const arrayResult = []; - for (let i = 0; i < arrayLen; i++) { - const int = i32Stack.pop(); - arrayResult.push(int); - } - arrayResult.reverse(); - optional = arrayResult; - } else { - optional = null; - } - return { tag: OptionalAllTypesResultValues.Tag.OptArray, param0: optional }; + optional = arrayResult; + } else { + optional = null; } - case OptionalAllTypesResultValues.Tag.Empty: return { tag: OptionalAllTypesResultValues.Tag.Empty }; - default: throw new Error("Unknown OptionalAllTypesResultValues tag returned from Swift: " + String(tag)); + return { tag: OptionalAllTypesResultValues.Tag.OptArray, param0: optional }; } + case OptionalAllTypesResultValues.Tag.Empty: return { tag: OptionalAllTypesResultValues.Tag.Empty }; + default: throw new Error("Unknown OptionalAllTypesResultValues tag returned from Swift: " + String(tag)); } - }); - }; + } + }); return { /** @@ -892,8 +873,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -1067,31 +1047,31 @@ export async function createInstantiator(options, swift) { } } - const PointHelpers = __bjs_createPointHelpers()(); + const PointHelpers = __bjs_createPointHelpers(); structHelpers.Point = PointHelpers; - const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers(); enumHelpers.APIResult = APIResultHelpers; - const ComplexResultHelpers = __bjs_createComplexResultValuesHelpers()(); + const ComplexResultHelpers = __bjs_createComplexResultValuesHelpers(); enumHelpers.ComplexResult = ComplexResultHelpers; - const ResultHelpers = __bjs_createResultValuesHelpers()(); + const ResultHelpers = __bjs_createResultValuesHelpers(); enumHelpers.Result = ResultHelpers; - const NetworkingResultHelpers = __bjs_createNetworkingResultValuesHelpers()(); + const NetworkingResultHelpers = __bjs_createNetworkingResultValuesHelpers(); enumHelpers.NetworkingResult = NetworkingResultHelpers; - const APIOptionalResultHelpers = __bjs_createAPIOptionalResultValuesHelpers()(); + const APIOptionalResultHelpers = __bjs_createAPIOptionalResultValuesHelpers(); enumHelpers.APIOptionalResult = APIOptionalResultHelpers; - const TypedPayloadResultHelpers = __bjs_createTypedPayloadResultValuesHelpers()(); + const TypedPayloadResultHelpers = __bjs_createTypedPayloadResultValuesHelpers(); enumHelpers.TypedPayloadResult = TypedPayloadResultHelpers; - const AllTypesResultHelpers = __bjs_createAllTypesResultValuesHelpers()(); + const AllTypesResultHelpers = __bjs_createAllTypesResultValuesHelpers(); enumHelpers.AllTypesResult = AllTypesResultHelpers; - const OptionalAllTypesResultHelpers = __bjs_createOptionalAllTypesResultValuesHelpers()(); + const OptionalAllTypesResultHelpers = __bjs_createOptionalAllTypesResultValuesHelpers(); enumHelpers.OptionalAllTypesResult = OptionalAllTypesResultHelpers; const exports = { @@ -1099,7 +1079,7 @@ export async function createInstantiator(options, swift) { handle: function bjs_handle(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.APIResult.lower(result); instance.exports.bjs_handle(resultCaseId); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); }, getResult: function bjs_getResult() { instance.exports.bjs_getResult(); @@ -1110,33 +1090,32 @@ export async function createInstantiator(options, swift) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.APIResult.lower(result); instance.exports.bjs_roundtripAPIResult(resultCaseId); const ret = enumHelpers.APIResult.lift(i32Stack.pop()); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); return ret; }, roundTripOptionalAPIResult: function bjs_roundTripOptionalAPIResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.APIResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalAPIResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.APIResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.APIResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalAPIResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.APIResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, handleComplex: function bjs_handleComplex(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.ComplexResult.lower(result); instance.exports.bjs_handleComplex(resultCaseId); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); }, getComplexResult: function bjs_getComplexResult() { instance.exports.bjs_getComplexResult(); @@ -1147,196 +1126,193 @@ export async function createInstantiator(options, swift) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.ComplexResult.lower(result); instance.exports.bjs_roundtripComplexResult(resultCaseId); const ret = enumHelpers.ComplexResult.lift(i32Stack.pop()); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); return ret; }, roundTripOptionalComplexResult: function bjs_roundTripOptionalComplexResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.ComplexResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalComplexResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.ComplexResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.ComplexResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalComplexResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.ComplexResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, roundTripOptionalUtilitiesResult: function bjs_roundTripOptionalUtilitiesResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.Result.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalUtilitiesResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.Result.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.Result.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalUtilitiesResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.Result.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, roundTripOptionalNetworkingResult: function bjs_roundTripOptionalNetworkingResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.NetworkingResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalNetworkingResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.NetworkingResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.NetworkingResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalNetworkingResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.NetworkingResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, roundTripOptionalAPIOptionalResult: function bjs_roundTripOptionalAPIOptionalResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.APIOptionalResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalAPIOptionalResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.APIOptionalResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.APIOptionalResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalAPIOptionalResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.APIOptionalResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, compareAPIResults: function bjs_compareAPIResults(result1, result2) { const isSome = result1 != null; - let result1CaseId, result1Cleanup; + let caseId; + let result1Cleanup1; if (isSome) { - const enumResult = enumHelpers.APIOptionalResult.lower(result1); - result1CaseId = enumResult.caseId; - result1Cleanup = enumResult.cleanup; + const { caseId: result1CaseId, cleanup: result1Cleanup } = enumHelpers.APIOptionalResult.lower(result1); + caseId = result1CaseId; + result1Cleanup1 = () => { + result1Cleanup && result1Cleanup(); + }; + } else { + caseId = 0; } const isSome1 = result2 != null; - let result2CaseId, result2Cleanup; + let caseId1; + let result2Cleanup1; if (isSome1) { - const enumResult1 = enumHelpers.APIOptionalResult.lower(result2); - result2CaseId = enumResult1.caseId; - result2Cleanup = enumResult1.cleanup; - } - instance.exports.bjs_compareAPIResults(+isSome, isSome ? result1CaseId : 0, +isSome1, isSome1 ? result2CaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: result2CaseId, cleanup: result2Cleanup } = enumHelpers.APIOptionalResult.lower(result2); + caseId1 = result2CaseId; + result2Cleanup1 = () => { + result2Cleanup && result2Cleanup(); + }; } else { - optResult = enumHelpers.APIOptionalResult.lift(tag); + caseId1 = 0; } - if (result1Cleanup) { result1Cleanup(); } - if (result2Cleanup) { result2Cleanup(); } + instance.exports.bjs_compareAPIResults(+isSome, caseId, +isSome1, caseId1); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.APIOptionalResult.lift(tag); + result1Cleanup1 && result1Cleanup1(); + result2Cleanup1 && result2Cleanup1(); return optResult; }, roundTripTypedPayloadResult: function bjs_roundTripTypedPayloadResult(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.TypedPayloadResult.lower(result); instance.exports.bjs_roundTripTypedPayloadResult(resultCaseId); const ret = enumHelpers.TypedPayloadResult.lift(i32Stack.pop()); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); return ret; }, roundTripOptionalTypedPayloadResult: function bjs_roundTripOptionalTypedPayloadResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.TypedPayloadResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalTypedPayloadResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.TypedPayloadResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.TypedPayloadResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalTypedPayloadResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.TypedPayloadResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, roundTripAllTypesResult: function bjs_roundTripAllTypesResult(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.AllTypesResult.lower(result); instance.exports.bjs_roundTripAllTypesResult(resultCaseId); const ret = enumHelpers.AllTypesResult.lift(i32Stack.pop()); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); return ret; }, roundTripOptionalAllTypesResult: function bjs_roundTripOptionalAllTypesResult(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.AllTypesResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalAllTypesResult(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.AllTypesResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.AllTypesResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalAllTypesResult(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.AllTypesResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, roundTripOptionalPayloadResult: function bjs_roundTripOptionalPayloadResult(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.OptionalAllTypesResult.lower(result); instance.exports.bjs_roundTripOptionalPayloadResult(resultCaseId); const ret = enumHelpers.OptionalAllTypesResult.lift(i32Stack.pop()); - if (resultCleanup) { resultCleanup(); } + resultCleanup && resultCleanup(); return ret; }, roundTripOptionalPayloadResultOpt: function bjs_roundTripOptionalPayloadResultOpt(result) { const isSome = result != null; - let resultCaseId, resultCleanup; + let caseId; + let resultCleanup1; if (isSome) { - const enumResult = enumHelpers.OptionalAllTypesResult.lower(result); - resultCaseId = enumResult.caseId; - resultCleanup = enumResult.cleanup; - } - instance.exports.bjs_roundTripOptionalPayloadResultOpt(+isSome, isSome ? resultCaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.OptionalAllTypesResult.lower(result); + caseId = resultCaseId; + resultCleanup1 = () => { + resultCleanup && resultCleanup(); + }; } else { - optResult = enumHelpers.OptionalAllTypesResult.lift(tag); + caseId = 0; } - if (resultCleanup) { resultCleanup(); } + instance.exports.bjs_roundTripOptionalPayloadResultOpt(+isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.OptionalAllTypesResult.lift(tag); + resultCleanup1 && resultCleanup1(); return optResult; }, APIResult: APIResultValues, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js index 72d78dbe0..189497e78 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.js @@ -100,8 +100,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js index 392788ef6..840dc9032 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Global.js @@ -120,8 +120,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js index 0a2826a5e..746248965 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.js @@ -101,8 +101,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js index b1ee0538f..a9fd41e92 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js @@ -152,8 +152,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -316,12 +315,17 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalTheme: function bjs_roundTripOptionalTheme(input) { const isSome = input != null; - let inputId, inputBytes; + let bytes, length; if (isSome) { - inputBytes = textEncoder.encode(input); - inputId = swift.memory.retain(inputBytes); + const inputBytes = textEncoder.encode(input); + const inputId = swift.memory.retain(inputBytes); + bytes = inputId; + length = inputBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripOptionalTheme(+isSome, isSome ? inputId : 0, isSome ? inputBytes.length : 0); + instance.exports.bjs_roundTripOptionalTheme(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; @@ -339,12 +343,17 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalTSTheme: function bjs_roundTripOptionalTSTheme(input) { const isSome = input != null; - let inputId, inputBytes; + let bytes, length; if (isSome) { - inputBytes = textEncoder.encode(input); - inputId = swift.memory.retain(inputBytes); + const inputBytes = textEncoder.encode(input); + const inputId = swift.memory.retain(inputBytes); + bytes = inputId; + length = inputBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripOptionalTSTheme(+isSome, isSome ? inputId : 0, isSome ? inputBytes.length : 0); + instance.exports.bjs_roundTripOptionalTSTheme(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; @@ -362,12 +371,17 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalFeatureFlag: function bjs_roundTripOptionalFeatureFlag(input) { const isSome = input != null; - let inputId, inputBytes; + let bytes, length; if (isSome) { - inputBytes = textEncoder.encode(input); - inputId = swift.memory.retain(inputBytes); + const inputBytes = textEncoder.encode(input); + const inputId = swift.memory.retain(inputBytes); + bytes = inputId; + length = inputBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripOptionalFeatureFlag(+isSome, isSome ? inputId : 0, isSome ? inputBytes.length : 0); + instance.exports.bjs_roundTripOptionalFeatureFlag(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; @@ -479,7 +493,7 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalPrecision: function bjs_roundTripOptionalPrecision(input) { const isSome = input != null; - instance.exports.bjs_roundTripOptionalPrecision(+isSome, isSome ? input : 0); + instance.exports.bjs_roundTripOptionalPrecision(+isSome, isSome ? input : 0.0); const optResult = tmpRetOptionalFloat; tmpRetOptionalFloat = undefined; return optResult; @@ -493,7 +507,7 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalRatio: function bjs_roundTripOptionalRatio(input) { const isSome = input != null; - instance.exports.bjs_roundTripOptionalRatio(+isSome, isSome ? input : 0); + instance.exports.bjs_roundTripOptionalRatio(+isSome, isSome ? input : 0.0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; return optResult; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js index 6f6bf9531..eba502334 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalGetter.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js index 9d57a04d3..0062e417f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GlobalThisImports.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js index 7c946a716..8107351fd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js index 5d724202b..c380da6e0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js @@ -29,56 +29,75 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createFooContainerHelpers = () => { - return () => ({ - lower: (value) => { - let id; - if (value.foo != null) { - id = swift.memory.retain(value.foo); - } else { - id = undefined; - } - i32Stack.push(id !== undefined ? id : 0); - const isSome = value.optionalFoo != null; + const __bjs_createFooContainerHelpers = () => ({ + lower: (value) => { + let id; + if (value.foo != null) { + id = swift.memory.retain(value.foo); + } else { + id = undefined; + } + i32Stack.push(id !== undefined ? id : 0); + const isSome = value.optionalFoo != null; + let innerCleanup; + if (isSome) { let id1; - if (isSome) { + if (value.optionalFoo != null) { id1 = swift.memory.retain(value.optionalFoo); - i32Stack.push(id1); } else { id1 = undefined; - i32Stack.push(0); } - i32Stack.push(isSome ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const objectId = i32Stack.pop(); - let value; - if (objectId !== 0) { - value = swift.memory.getObject(objectId); - swift.memory.release(objectId); - } else { - value = null; + i32Stack.push(id1 !== undefined ? id1 : 0); + innerCleanup = () => { + if(id1 !== undefined && id1 !== 0) { + try { + swift.memory.getObject(id1); + swift.memory.release(id1); + } catch(e) { console.warn('BridgeJS: cleanup failed for retained object', e); } } - optional = value; - } else { - optional = null; + }; + } else { + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + if(id !== undefined && id !== 0) { + try { + swift.memory.getObject(id); + swift.memory.release(id); + } catch(e) { console.warn('BridgeJS: cleanup failed for retained object', e); } } - const objectId1 = i32Stack.pop(); - let value1; - if (objectId1 !== 0) { - value1 = swift.memory.getObject(objectId1); - swift.memory.release(objectId1); + innerCleanup && innerCleanup(); + }; + return { cleanup }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const objectId = i32Stack.pop(); + let value; + if (objectId !== 0) { + value = swift.memory.getObject(objectId); + swift.memory.release(objectId); } else { - value1 = null; + value = null; } - return { foo: value1, optionalFoo: optional }; - } - }); - }; + optional = value; + } else { + optional = null; + } + const objectId1 = i32Stack.pop(); + let value1; + if (objectId1 !== 0) { + value1 = swift.memory.getObject(objectId1); + swift.memory.release(objectId1); + } else { + value1 = null; + } + return { foo: value1, optionalFoo: optional }; + } + }); return { /** @@ -127,8 +146,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -278,7 +296,7 @@ export async function createInstantiator(options, swift) { /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { const js = swift.memory.heap; - const FooContainerHelpers = __bjs_createFooContainerHelpers()(); + const FooContainerHelpers = __bjs_createFooContainerHelpers(); structHelpers.FooContainer = FooContainerHelpers; const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js index 54a0f2018..b7b1684c0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/InvalidPropertyNames.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js index a192a1a8b..369a63492 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClass.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js index 1166d1f62..f1f9e573e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSClassStaticFunctions.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js index d45efc771..a3dd3649c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js @@ -166,8 +166,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -370,15 +369,35 @@ export async function createInstantiator(options, swift) { constructor(value, optionalValue) { const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); const isSome = optionalValue != null; - const [optionalValueKind, optionalValuePayload1, optionalValuePayload2] = __bjs_jsValueLower(optionalValue); - const ret = instance.exports.bjs_JSValueHolder_init(valueKind, valuePayload1, valuePayload2, +isSome, optionalValueKind, optionalValuePayload1, optionalValuePayload2); + let kind, payload1, payload2; + if (isSome) { + const [optionalValueKind, optionalValuePayload1, optionalValuePayload2] = __bjs_jsValueLower(optionalValue); + kind = optionalValueKind; + payload1 = optionalValuePayload1; + payload2 = optionalValuePayload2; + } else { + kind = 0; + payload1 = 0; + payload2 = 0.0; + } + const ret = instance.exports.bjs_JSValueHolder_init(valueKind, valuePayload1, valuePayload2, +isSome, kind, payload1, payload2); return JSValueHolder.__construct(ret); } update(value, optionalValue) { const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); const isSome = optionalValue != null; - const [optionalValueKind, optionalValuePayload1, optionalValuePayload2] = __bjs_jsValueLower(optionalValue); - instance.exports.bjs_JSValueHolder_update(this.pointer, valueKind, valuePayload1, valuePayload2, +isSome, optionalValueKind, optionalValuePayload1, optionalValuePayload2); + let kind, payload1, payload2; + if (isSome) { + const [optionalValueKind, optionalValuePayload1, optionalValuePayload2] = __bjs_jsValueLower(optionalValue); + kind = optionalValueKind; + payload1 = optionalValuePayload1; + payload2 = optionalValuePayload2; + } else { + kind = 0; + payload1 = 0; + payload2 = 0.0; + } + instance.exports.bjs_JSValueHolder_update(this.pointer, valueKind, valuePayload1, valuePayload2, +isSome, kind, payload1, payload2); } echo(value) { const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); @@ -391,8 +410,18 @@ export async function createInstantiator(options, swift) { } echoOptional(value) { const isSome = value != null; - const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); - instance.exports.bjs_JSValueHolder_echoOptional(this.pointer, +isSome, valueKind, valuePayload1, valuePayload2); + let kind, payload1, payload2; + if (isSome) { + const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); + kind = valueKind; + payload1 = valuePayload1; + payload2 = valuePayload2; + } else { + kind = 0; + payload1 = 0; + payload2 = 0.0; + } + instance.exports.bjs_JSValueHolder_echoOptional(this.pointer, +isSome, kind, payload1, payload2); const isSome1 = i32Stack.pop(); let optResult; if (isSome1) { @@ -435,8 +464,18 @@ export async function createInstantiator(options, swift) { } set optionalValue(value) { const isSome = value != null; - const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); - instance.exports.bjs_JSValueHolder_optionalValue_set(this.pointer, +isSome, valueKind, valuePayload1, valuePayload2); + let kind, payload1, payload2; + if (isSome) { + const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); + kind = valueKind; + payload1 = valuePayload1; + payload2 = valuePayload2; + } else { + kind = 0; + payload1 = 0; + payload2 = 0.0; + } + instance.exports.bjs_JSValueHolder_optionalValue_set(this.pointer, +isSome, kind, payload1, payload2); } } const exports = { @@ -452,8 +491,18 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalJSValue: function bjs_roundTripOptionalJSValue(value) { const isSome = value != null; - const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); - instance.exports.bjs_roundTripOptionalJSValue(+isSome, valueKind, valuePayload1, valuePayload2); + let kind, payload1, payload2; + if (isSome) { + const [valueKind, valuePayload1, valuePayload2] = __bjs_jsValueLower(value); + kind = valueKind; + payload1 = valuePayload1; + payload2 = valuePayload2; + } else { + kind = 0; + payload1 = 0; + payload2 = 0.0; + } + instance.exports.bjs_roundTripOptionalJSValue(+isSome, kind, payload1, payload2); const isSome1 = i32Stack.pop(); let optResult; if (isSome1) { @@ -492,7 +541,7 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalJSValueArray: function bjs_roundTripOptionalJSValueArray(values) { const isSome = values != null; - const valuesCleanups = []; + let valuesCleanup; if (isSome) { const arrayCleanups = []; for (const elem of values) { @@ -502,7 +551,9 @@ export async function createInstantiator(options, swift) { f64Stack.push(elemPayload2); } i32Stack.push(values.length); - valuesCleanups.push(() => { for (const cleanup of arrayCleanups) { cleanup(); } }); + valuesCleanup = () => { + for (const cleanup of arrayCleanups) { cleanup(); } + }; } i32Stack.push(+isSome); instance.exports.bjs_roundTripOptionalJSValueArray(); @@ -523,7 +574,7 @@ export async function createInstantiator(options, swift) { } else { optResult = null; } - for (const cleanup of valuesCleanups) { cleanup(); } + valuesCleanup && valuesCleanup(); return optResult; }, }; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js index 675aa3798..49cceffe4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedGlobal.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js index 6a6c75ef4..959f4e278 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedModules.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js index f36211310..13215f197 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/MixedPrivate.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js index 09ad002a1..d075e4c39 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js index 7368e52fa..a9e0251a7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js index c4f99c84b..41c6a094b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -209,19 +208,25 @@ export async function createInstantiator(options, swift) { return swift.memory.retain(obj); }; const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; - TestModule["bjs_WithOptionalJSClass_init"] = function bjs_WithOptionalJSClass_init(valueOrNullIsSome, valueOrNullWrappedValue, valueOrUndefinedIsSome, valueOrUndefinedWrappedValue) { + TestModule["bjs_WithOptionalJSClass_init"] = function bjs_WithOptionalJSClass_init(valueOrNullIsSome, valueOrNullObjectId, valueOrUndefinedIsSome, valueOrUndefinedObjectId) { try { - let obj; + let optResult; if (valueOrNullIsSome) { - obj = swift.memory.getObject(valueOrNullWrappedValue); - swift.memory.release(valueOrNullWrappedValue); + const valueOrNullObjectIdObject = swift.memory.getObject(valueOrNullObjectId); + swift.memory.release(valueOrNullObjectId); + optResult = valueOrNullObjectIdObject; + } else { + optResult = null; } - let obj1; + let optResult1; if (valueOrUndefinedIsSome) { - obj1 = swift.memory.getObject(valueOrUndefinedWrappedValue); - swift.memory.release(valueOrUndefinedWrappedValue); + const valueOrUndefinedObjectIdObject = swift.memory.getObject(valueOrUndefinedObjectId); + swift.memory.release(valueOrUndefinedObjectId); + optResult1 = valueOrUndefinedObjectIdObject; + } else { + optResult1 = undefined; } - return swift.memory.retain(new imports.WithOptionalJSClass(valueOrNullIsSome ? obj : null, valueOrUndefinedIsSome ? obj1 : undefined)); + return swift.memory.retain(new imports.WithOptionalJSClass(optResult, optResult1)); } catch (error) { setException(error); return 0 @@ -231,11 +236,7 @@ export async function createInstantiator(options, swift) { try { let ret = swift.memory.getObject(self).stringOrNull; const isSome = ret != null; - if (isSome) { - tmpRetString = ret; - } else { - tmpRetString = null; - } + tmpRetString = isSome ? ret : null; } catch (error) { setException(error); } @@ -244,11 +245,7 @@ export async function createInstantiator(options, swift) { try { let ret = swift.memory.getObject(self).stringOrUndefined; const isSome = ret !== undefined; - if (isSome) { - tmpRetString = ret; - } else { - tmpRetString = null; - } + tmpRetString = isSome ? ret : undefined; } catch (error) { setException(error); } @@ -307,26 +304,32 @@ export async function createInstantiator(options, swift) { setException(error); } } - TestModule["bjs_WithOptionalJSClass_stringOrNull_set"] = function bjs_WithOptionalJSClass_stringOrNull_set(self, newValueIsSome, newValueWrappedValue) { + TestModule["bjs_WithOptionalJSClass_stringOrNull_set"] = function bjs_WithOptionalJSClass_stringOrNull_set(self, newValueIsSome, newValueObjectId) { try { - let obj; + let optResult; if (newValueIsSome) { - obj = swift.memory.getObject(newValueWrappedValue); - swift.memory.release(newValueWrappedValue); + const newValueObjectIdObject = swift.memory.getObject(newValueObjectId); + swift.memory.release(newValueObjectId); + optResult = newValueObjectIdObject; + } else { + optResult = null; } - swift.memory.getObject(self).stringOrNull = newValueIsSome ? obj : null; + swift.memory.getObject(self).stringOrNull = optResult; } catch (error) { setException(error); } } - TestModule["bjs_WithOptionalJSClass_stringOrUndefined_set"] = function bjs_WithOptionalJSClass_stringOrUndefined_set(self, newValueIsSome, newValueWrappedValue) { + TestModule["bjs_WithOptionalJSClass_stringOrUndefined_set"] = function bjs_WithOptionalJSClass_stringOrUndefined_set(self, newValueIsSome, newValueObjectId) { try { - let obj; + let optResult; if (newValueIsSome) { - obj = swift.memory.getObject(newValueWrappedValue); - swift.memory.release(newValueWrappedValue); + const newValueObjectIdObject = swift.memory.getObject(newValueObjectId); + swift.memory.release(newValueObjectId); + optResult = newValueObjectIdObject; + } else { + optResult = undefined; } - swift.memory.getObject(self).stringOrUndefined = newValueIsSome ? obj : undefined; + swift.memory.getObject(self).stringOrUndefined = optResult; } catch (error) { setException(error); } @@ -373,38 +376,36 @@ export async function createInstantiator(options, swift) { setException(error); } } - TestModule["bjs_WithOptionalJSClass_roundTripStringOrNull"] = function bjs_WithOptionalJSClass_roundTripStringOrNull(self, valueIsSome, valueWrappedValue) { + TestModule["bjs_WithOptionalJSClass_roundTripStringOrNull"] = function bjs_WithOptionalJSClass_roundTripStringOrNull(self, valueIsSome, valueObjectId) { try { - let obj; + let optResult; 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) { - tmpRetString = ret; + const valueObjectIdObject = swift.memory.getObject(valueObjectId); + swift.memory.release(valueObjectId); + optResult = valueObjectIdObject; } else { - tmpRetString = null; + optResult = null; } + let ret = swift.memory.getObject(self).roundTripStringOrNull(optResult); + const isSome = ret != null; + tmpRetString = isSome ? ret : null; } catch (error) { setException(error); } } - TestModule["bjs_WithOptionalJSClass_roundTripStringOrUndefined"] = function bjs_WithOptionalJSClass_roundTripStringOrUndefined(self, valueIsSome, valueWrappedValue) { + TestModule["bjs_WithOptionalJSClass_roundTripStringOrUndefined"] = function bjs_WithOptionalJSClass_roundTripStringOrUndefined(self, valueIsSome, valueObjectId) { try { - let obj; + let optResult; 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) { - tmpRetString = ret; + const valueObjectIdObject = swift.memory.getObject(valueObjectId); + swift.memory.release(valueObjectId); + optResult = valueObjectIdObject; } else { - tmpRetString = null; + optResult = undefined; } + let ret = swift.memory.getObject(self).roundTripStringOrUndefined(optResult); + const isSome = ret !== undefined; + tmpRetString = isSome ? ret : undefined; } catch (error) { setException(error); } @@ -501,12 +502,17 @@ export async function createInstantiator(options, swift) { constructor(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - const ret = instance.exports.bjs_Greeter_init(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + const ret = instance.exports.bjs_Greeter_init(+isSome, bytes, length); return Greeter.__construct(ret); } greet() { @@ -517,12 +523,17 @@ export async function createInstantiator(options, swift) { } changeName(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_Greeter_changeName(this.pointer, +isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_Greeter_changeName(this.pointer, +isSome, bytes, length); } get name() { instance.exports.bjs_Greeter_name_get(this.pointer); @@ -532,12 +543,17 @@ export async function createInstantiator(options, swift) { } set name(value) { const isSome = value != null; - let valueId, valueBytes; + let bytes, length; if (isSome) { - valueBytes = textEncoder.encode(value); - valueId = swift.memory.retain(valueBytes); + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + bytes = valueId; + length = valueBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_Greeter_name_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + instance.exports.bjs_Greeter_name_set(this.pointer, +isSome, bytes, length); } } class OptionalPropertyHolder extends SwiftHeapObject { @@ -557,12 +573,17 @@ export async function createInstantiator(options, swift) { } set optionalName(value) { const isSome = value != null; - let valueId, valueBytes; + let bytes, length; if (isSome) { - valueBytes = textEncoder.encode(value); - valueId = swift.memory.retain(valueBytes); + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + bytes = valueId; + length = valueBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_OptionalPropertyHolder_optionalName_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + instance.exports.bjs_OptionalPropertyHolder_optionalName_set(this.pointer, +isSome, bytes, length); } get optionalAge() { instance.exports.bjs_OptionalPropertyHolder_optionalAge_get(this.pointer); @@ -583,7 +604,13 @@ export async function createInstantiator(options, swift) { } set optionalGreeter(value) { const isSome = value != null; - instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_set(this.pointer, +isSome, isSome ? value.pointer : 0); + let pointer; + if (isSome) { + pointer = value.pointer; + } else { + pointer = 0; + } + instance.exports.bjs_OptionalPropertyHolder_optionalGreeter_set(this.pointer, +isSome, pointer); } } const exports = { @@ -591,28 +618,45 @@ export async function createInstantiator(options, swift) { OptionalPropertyHolder, roundTripOptionalClass: function bjs_roundTripOptionalClass(value) { const isSome = value != null; - instance.exports.bjs_roundTripOptionalClass(+isSome, isSome ? value.pointer : 0); - const pointer = tmpRetOptionalHeapObject; + let pointer; + if (isSome) { + pointer = value.pointer; + } else { + pointer = 0; + } + instance.exports.bjs_roundTripOptionalClass(+isSome, pointer); + const pointer1 = tmpRetOptionalHeapObject; tmpRetOptionalHeapObject = undefined; - const optResult = pointer === null ? null : Greeter.__construct(pointer); + const optResult = pointer1 === null ? null : Greeter.__construct(pointer1); return optResult; }, testOptionalPropertyRoundtrip: function bjs_testOptionalPropertyRoundtrip(holder) { const isSome = holder != null; - instance.exports.bjs_testOptionalPropertyRoundtrip(+isSome, isSome ? holder.pointer : 0); - const pointer = tmpRetOptionalHeapObject; + let pointer; + if (isSome) { + pointer = holder.pointer; + } else { + pointer = 0; + } + instance.exports.bjs_testOptionalPropertyRoundtrip(+isSome, pointer); + const pointer1 = tmpRetOptionalHeapObject; tmpRetOptionalHeapObject = undefined; - const optResult = pointer === null ? null : OptionalPropertyHolder.__construct(pointer); + const optResult = pointer1 === null ? null : OptionalPropertyHolder.__construct(pointer1); return optResult; }, roundTripString: function bjs_roundTripString(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripString(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripString(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; @@ -626,76 +670,96 @@ export async function createInstantiator(options, swift) { }, roundTripBool: function bjs_roundTripBool(flag) { const isSome = flag != null; - instance.exports.bjs_roundTripBool(+isSome, isSome ? flag : 0); + instance.exports.bjs_roundTripBool(+isSome, isSome ? flag ? 1 : 0 : 0); const optResult = tmpRetOptionalBool; tmpRetOptionalBool = undefined; return optResult; }, roundTripFloat: function bjs_roundTripFloat(number) { const isSome = number != null; - instance.exports.bjs_roundTripFloat(+isSome, isSome ? number : 0); + instance.exports.bjs_roundTripFloat(+isSome, isSome ? number : 0.0); const optResult = tmpRetOptionalFloat; tmpRetOptionalFloat = undefined; return optResult; }, roundTripDouble: function bjs_roundTripDouble(precision) { const isSome = precision != null; - instance.exports.bjs_roundTripDouble(+isSome, isSome ? precision : 0); + instance.exports.bjs_roundTripDouble(+isSome, isSome ? precision : 0.0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; return optResult; }, roundTripSyntax: function bjs_roundTripSyntax(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripSyntax(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, roundTripMixSyntax: function bjs_roundTripMixSyntax(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripMixSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripMixSyntax(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, roundTripSwiftSyntax: function bjs_roundTripSwiftSyntax(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripSwiftSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripSwiftSyntax(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, roundTripMixedSwiftSyntax: function bjs_roundTripMixedSwiftSyntax(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripMixedSwiftSyntax(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripMixedSwiftSyntax(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, roundTripWithSpaces: function bjs_roundTripWithSpaces(value) { const isSome = value != null; - instance.exports.bjs_roundTripWithSpaces(+isSome, isSome ? value : 0); + instance.exports.bjs_roundTripWithSpaces(+isSome, isSome ? value : 0.0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; return optResult; @@ -709,31 +773,46 @@ export async function createInstantiator(options, swift) { }, roundTripOptionalAlias: function bjs_roundTripOptionalAlias(name) { const isSome = name != null; - let nameId, nameBytes; + let bytes, length; if (isSome) { - nameBytes = textEncoder.encode(name); - nameId = swift.memory.retain(nameBytes); + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + bytes = nameId; + length = nameBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_roundTripOptionalAlias(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + instance.exports.bjs_roundTripOptionalAlias(+isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; return optResult; }, testMixedOptionals: function bjs_testMixedOptionals(firstName, lastName, age, active) { const isSome = firstName != null; - let firstNameId, firstNameBytes; + let bytes, length; if (isSome) { - firstNameBytes = textEncoder.encode(firstName); - firstNameId = swift.memory.retain(firstNameBytes); + const firstNameBytes = textEncoder.encode(firstName); + const firstNameId = swift.memory.retain(firstNameBytes); + bytes = firstNameId; + length = firstNameBytes.length; + } else { + bytes = 0; + length = 0; } const isSome1 = lastName != null; - let lastNameId, lastNameBytes; + let bytes1, length1; if (isSome1) { - lastNameBytes = textEncoder.encode(lastName); - lastNameId = swift.memory.retain(lastNameBytes); + const lastNameBytes = textEncoder.encode(lastName); + const lastNameId = swift.memory.retain(lastNameBytes); + bytes1 = lastNameId; + length1 = lastNameBytes.length; + } else { + bytes1 = 0; + length1 = 0; } const isSome2 = age != null; - instance.exports.bjs_testMixedOptionals(+isSome, isSome ? firstNameId : 0, isSome ? firstNameBytes.length : 0, +isSome1, isSome1 ? lastNameId : 0, isSome1 ? lastNameBytes.length : 0, +isSome2, isSome2 ? age : 0, active); + instance.exports.bjs_testMixedOptionals(+isSome, bytes, length, +isSome1, bytes1, length1, +isSome2, isSome2 ? age : 0, active); const optResult = tmpRetString; tmpRetString = undefined; return optResult; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js index 83c77397a..caabb1a61 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js index f1fda2cb7..aee604eb3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js index b39362233..61a93d34c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js index 1d413db20..015c2f1ce 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js @@ -53,43 +53,41 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case ResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: ResultValues.Tag.Success, cleanup }; - } - case ResultValues.Tag.Failure: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: ResultValues.Tag.Failure, cleanup }; - } - default: throw new Error("Unknown ResultValues tag: " + String(enumTag)); - } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case ResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: ResultValues.Tag.Success, param0: string }; - } - case ResultValues.Tag.Failure: { - const int = i32Stack.pop(); - return { tag: ResultValues.Tag.Failure, param0: int }; - } - default: throw new Error("Unknown ResultValues tag returned from Swift: " + String(tag)); + const __bjs_createResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case ResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: ResultValues.Tag.Success, cleanup }; + } + case ResultValues.Tag.Failure: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: ResultValues.Tag.Failure, cleanup }; + } + default: throw new Error("Unknown ResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case ResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: ResultValues.Tag.Success, param0: string }; + } + case ResultValues.Tag.Failure: { + const int = i32Stack.pop(); + return { tag: ResultValues.Tag.Failure, param0: int }; } + default: throw new Error("Unknown ResultValues tag returned from Swift: " + String(tag)); } - }); - }; + } + }); return { /** @@ -137,8 +135,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -306,14 +303,17 @@ export async function createInstantiator(options, swift) { setException(error); } } - TestModule["bjs_MyViewControllerDelegate_optionalName_set"] = function bjs_MyViewControllerDelegate_optionalName_set(self, valueIsSome, valueWrappedValue) { + TestModule["bjs_MyViewControllerDelegate_optionalName_set"] = function bjs_MyViewControllerDelegate_optionalName_set(self, valueIsSome, valueObjectId) { try { - let obj; + let optResult; if (valueIsSome) { - obj = swift.memory.getObject(valueWrappedValue); - swift.memory.release(valueWrappedValue); + const valueObjectIdObject = swift.memory.getObject(valueObjectId); + swift.memory.release(valueObjectId); + optResult = valueObjectIdObject; + } else { + optResult = null; } - swift.memory.getObject(self).optionalName = valueIsSome ? obj : null; + swift.memory.getObject(self).optionalName = optResult; } catch (error) { setException(error); } @@ -326,14 +326,17 @@ export async function createInstantiator(options, swift) { setException(error); } } - TestModule["bjs_MyViewControllerDelegate_optionalRawEnum_set"] = function bjs_MyViewControllerDelegate_optionalRawEnum_set(self, valueIsSome, valueWrappedValue) { + TestModule["bjs_MyViewControllerDelegate_optionalRawEnum_set"] = function bjs_MyViewControllerDelegate_optionalRawEnum_set(self, valueIsSome, valueObjectId) { try { - let obj; + let optResult; if (valueIsSome) { - obj = swift.memory.getObject(valueWrappedValue); - swift.memory.release(valueWrappedValue); + const valueObjectIdObject = swift.memory.getObject(valueObjectId); + swift.memory.release(valueObjectId); + optResult = valueObjectIdObject; + } else { + optResult = null; } - swift.memory.getObject(self).optionalRawEnum = valueIsSome ? obj : null; + swift.memory.getObject(self).optionalRawEnum = optResult; } catch (error) { setException(error); } @@ -359,7 +362,7 @@ export async function createInstantiator(options, swift) { TestModule["bjs_MyViewControllerDelegate_result_get"] = function bjs_MyViewControllerDelegate_result_get(self) { try { let ret = swift.memory.getObject(self).result; - const { caseId: caseId, cleanup: cleanup } = enumHelpers.Result.lower(ret); + const { caseId: caseId } = enumHelpers.Result.lower(ret); return caseId; } catch (error) { setException(error); @@ -378,7 +381,7 @@ export async function createInstantiator(options, swift) { let ret = swift.memory.getObject(self).optionalResult; const isSome = ret != null; if (isSome) { - const { caseId: caseId, cleanup: cleanup } = enumHelpers.Result.lower(ret); + const { caseId: caseId } = enumHelpers.Result.lower(ret); return caseId; } else { return -1; @@ -387,13 +390,16 @@ export async function createInstantiator(options, swift) { setException(error); } } - TestModule["bjs_MyViewControllerDelegate_optionalResult_set"] = function bjs_MyViewControllerDelegate_optionalResult_set(self, valueIsSome, valueWrappedValue) { + TestModule["bjs_MyViewControllerDelegate_optionalResult_set"] = function bjs_MyViewControllerDelegate_optionalResult_set(self, valueIsSome, valueCaseId) { try { - let enumValue; + let optResult; if (valueIsSome) { - enumValue = enumHelpers.Result.lift(valueWrappedValue); + const enumValue = enumHelpers.Result.lift(valueCaseId); + optResult = enumValue; + } else { + optResult = null; } - swift.memory.getObject(self).optionalResult = valueIsSome ? enumValue : null; + swift.memory.getObject(self).optionalResult = optResult; } catch (error) { setException(error); } @@ -418,7 +424,7 @@ export async function createInstantiator(options, swift) { try { let ret = swift.memory.getObject(self).directionOptional; const isSome = ret != null; - return isSome ? (ret | 0) : -1; + return isSome ? ret : -1; } catch (error) { setException(error); } @@ -522,9 +528,9 @@ export async function createInstantiator(options, swift) { return 0 } } - TestModule["bjs_MyViewControllerDelegate_onOptionalHelperUpdated"] = function bjs_MyViewControllerDelegate_onOptionalHelperUpdated(self, helperIsSome, helperWrappedValue) { + TestModule["bjs_MyViewControllerDelegate_onOptionalHelperUpdated"] = function bjs_MyViewControllerDelegate_onOptionalHelperUpdated(self, helperIsSome, helperPointer) { try { - swift.memory.getObject(self).onOptionalHelperUpdated(helperIsSome ? _exports['Helper'].__construct(helperWrappedValue) : null); + swift.memory.getObject(self).onOptionalHelperUpdated(helperIsSome ? _exports['Helper'].__construct(helperPointer) : null); } catch (error) { setException(error); } @@ -558,7 +564,7 @@ export async function createInstantiator(options, swift) { TestModule["bjs_MyViewControllerDelegate_getResult"] = function bjs_MyViewControllerDelegate_getResult(self) { try { let ret = swift.memory.getObject(self).getResult(); - const { caseId: caseId, cleanup: cleanup } = enumHelpers.Result.lower(ret); + const { caseId: caseId } = enumHelpers.Result.lower(ret); return caseId; } catch (error) { setException(error); @@ -667,7 +673,13 @@ export async function createInstantiator(options, swift) { } set secondDelegate(value) { const isSome = value != null; - instance.exports.bjs_MyViewController_secondDelegate_set(this.pointer, +isSome, isSome ? swift.memory.retain(value) : 0); + let value1; + if (isSome) { + value1 = swift.memory.retain(value); + } else { + value1 = 0; + } + instance.exports.bjs_MyViewController_secondDelegate_set(this.pointer, +isSome, value1); } } class DelegateManager extends SwiftHeapObject { @@ -713,7 +725,7 @@ export async function createInstantiator(options, swift) { for (const cleanup of arrayCleanups) { cleanup(); } } } - const ResultHelpers = __bjs_createResultValuesHelpers()(); + const ResultHelpers = __bjs_createResultValuesHelpers(); enumHelpers.Result = ResultHelpers; const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js index 07f7a3c77..2d4bd9659 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Global.js @@ -40,43 +40,41 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createAPIResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case APIResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Success, cleanup }; - } - case APIResultValues.Tag.Failure: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Failure, cleanup }; - } - default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + const __bjs_createAPIResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Success, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case APIResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: APIResultValues.Tag.Success, param0: string }; - } - case APIResultValues.Tag.Failure: { - const int = i32Stack.pop(); - return { tag: APIResultValues.Tag.Failure, param0: int }; - } - default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); + case APIResultValues.Tag.Failure: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Failure, cleanup }; + } + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case APIResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: APIResultValues.Tag.Success, param0: string }; + } + case APIResultValues.Tag.Failure: { + const int = i32Stack.pop(); + return { tag: APIResultValues.Tag.Failure, param0: int }; } + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); } - }); - }; + } + }); return { /** @@ -124,8 +122,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -304,7 +301,7 @@ export async function createInstantiator(options, swift) { return ret; } } - const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers(); enumHelpers.APIResult = APIResultHelpers; if (typeof globalThis.Utils === 'undefined') { @@ -328,7 +325,7 @@ export async function createInstantiator(options, swift) { const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); const ret = enumHelpers.APIResult.lift(i32Stack.pop()); - if (valueCleanup) { valueCleanup(); } + valueCleanup && valueCleanup(); return ret; } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js index c2c3f9a70..c3c39c581 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.js @@ -40,43 +40,41 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createAPIResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case APIResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Success, cleanup }; - } - case APIResultValues.Tag.Failure: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Failure, cleanup }; - } - default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + const __bjs_createAPIResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Success, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case APIResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: APIResultValues.Tag.Success, param0: string }; - } - case APIResultValues.Tag.Failure: { - const int = i32Stack.pop(); - return { tag: APIResultValues.Tag.Failure, param0: int }; - } - default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); + case APIResultValues.Tag.Failure: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Failure, cleanup }; + } + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + } + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case APIResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: APIResultValues.Tag.Success, param0: string }; + } + case APIResultValues.Tag.Failure: { + const int = i32Stack.pop(); + return { tag: APIResultValues.Tag.Failure, param0: int }; } + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); } - }); - }; + } + }); return { /** @@ -124,8 +122,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -304,7 +301,7 @@ export async function createInstantiator(options, swift) { return ret; } } - const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers(); enumHelpers.APIResult = APIResultHelpers; const exports = { @@ -322,7 +319,7 @@ export async function createInstantiator(options, swift) { const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); const ret = enumHelpers.APIResult.lift(i32Stack.pop()); - if (valueCleanup) { valueCleanup(); } + valueCleanup && valueCleanup(); return ret; } }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js index 3784b5a7e..cbd393d1a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Global.js @@ -81,8 +81,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -304,12 +303,17 @@ export async function createInstantiator(options, swift) { } static set optionalProperty(value) { const isSome = value != null; - let valueId, valueBytes; + let bytes, length; if (isSome) { - valueBytes = textEncoder.encode(value); - valueId = swift.memory.retain(valueBytes); + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + bytes = valueId; + length = valueBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_PropertyClass_static_optionalProperty_set(+isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + instance.exports.bjs_PropertyClass_static_optionalProperty_set(+isSome, bytes, length); } } if (typeof globalThis.PropertyNamespace === 'undefined') { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js index 47f0092af..825cdbb8c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.js @@ -81,8 +81,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -304,12 +303,17 @@ export async function createInstantiator(options, swift) { } static set optionalProperty(value) { const isSome = value != null; - let valueId, valueBytes; + let bytes, length; if (isSome) { - valueBytes = textEncoder.encode(value); - valueId = swift.memory.retain(valueBytes); + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + bytes = valueId; + length = valueBytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.bjs_PropertyClass_static_optionalProperty_set(+isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + instance.exports.bjs_PropertyClass_static_optionalProperty_set(+isSome, bytes, length); } } const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js index 0f92074d4..b91465144 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js index f86fce218..4fabd5f00 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js index 9f8f93c5b..6ff51a438 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -222,9 +221,9 @@ export async function createInstantiator(options, swift) { return 0 } } - TestModule["bjs_jsRoundTripOptionalGreeter"] = function bjs_jsRoundTripOptionalGreeter(greeterIsSome, greeterWrappedValue) { + TestModule["bjs_jsRoundTripOptionalGreeter"] = function bjs_jsRoundTripOptionalGreeter(greeterIsSome, greeterPointer) { try { - let ret = imports.jsRoundTripOptionalGreeter(greeterIsSome ? _exports['Greeter'].__construct(greeterWrappedValue) : null); + let ret = imports.jsRoundTripOptionalGreeter(greeterIsSome ? _exports['Greeter'].__construct(greeterPointer) : null); const isSome = ret != null; return isSome ? ret.pointer : 0; } catch (error) { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js index 3371cf1b3..d1d6c2afd 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js @@ -84,75 +84,73 @@ export async function createInstantiator(options, swift) { return swift.memory.retain(real); }; - const __bjs_createAPIResultValuesHelpers = () => { - return () => ({ - lower: (value) => { - const enumTag = value.tag; - switch (enumTag) { - case APIResultValues.Tag.Success: { - const bytes = textEncoder.encode(value.param0); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Success, cleanup }; - } - case APIResultValues.Tag.Failure: { - i32Stack.push((value.param0 | 0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Failure, cleanup }; - } - case APIResultValues.Tag.Flag: { - i32Stack.push(value.param0 ? 1 : 0); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Flag, cleanup }; - } - case APIResultValues.Tag.Rate: { - f32Stack.push(Math.fround(value.param0)); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Rate, cleanup }; - } - case APIResultValues.Tag.Precise: { - f64Stack.push(value.param0); - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Precise, cleanup }; - } - case APIResultValues.Tag.Info: { - const cleanup = undefined; - return { caseId: APIResultValues.Tag.Info, cleanup }; - } - default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); + const __bjs_createAPIResultValuesHelpers = () => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResultValues.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Success, cleanup }; } - }, - lift: (tag) => { - tag = tag | 0; - switch (tag) { - case APIResultValues.Tag.Success: { - const string = strStack.pop(); - return { tag: APIResultValues.Tag.Success, param0: string }; - } - case APIResultValues.Tag.Failure: { - const int = i32Stack.pop(); - return { tag: APIResultValues.Tag.Failure, param0: int }; - } - case APIResultValues.Tag.Flag: { - const bool = i32Stack.pop() !== 0; - return { tag: APIResultValues.Tag.Flag, param0: bool }; - } - case APIResultValues.Tag.Rate: { - const f32 = f32Stack.pop(); - return { tag: APIResultValues.Tag.Rate, param0: f32 }; - } - case APIResultValues.Tag.Precise: { - const f64 = f64Stack.pop(); - return { tag: APIResultValues.Tag.Precise, param0: f64 }; - } - case APIResultValues.Tag.Info: return { tag: APIResultValues.Tag.Info }; - default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); + case APIResultValues.Tag.Failure: { + i32Stack.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Failure, cleanup }; + } + case APIResultValues.Tag.Flag: { + i32Stack.push(value.param0 ? 1 : 0); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Flag, cleanup }; + } + case APIResultValues.Tag.Rate: { + f32Stack.push(Math.fround(value.param0)); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Rate, cleanup }; } + case APIResultValues.Tag.Precise: { + f64Stack.push(value.param0); + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Precise, cleanup }; + } + case APIResultValues.Tag.Info: { + const cleanup = undefined; + return { caseId: APIResultValues.Tag.Info, cleanup }; + } + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); } - }); - }; + }, + lift: (tag) => { + tag = tag | 0; + switch (tag) { + case APIResultValues.Tag.Success: { + const string = strStack.pop(); + return { tag: APIResultValues.Tag.Success, param0: string }; + } + case APIResultValues.Tag.Failure: { + const int = i32Stack.pop(); + return { tag: APIResultValues.Tag.Failure, param0: int }; + } + case APIResultValues.Tag.Flag: { + const bool = i32Stack.pop() !== 0; + return { tag: APIResultValues.Tag.Flag, param0: bool }; + } + case APIResultValues.Tag.Rate: { + const f32 = f32Stack.pop(); + return { tag: APIResultValues.Tag.Rate, param0: f32 }; + } + case APIResultValues.Tag.Precise: { + const f64 = f64Stack.pop(); + return { tag: APIResultValues.Tag.Precise, param0: f64 }; + } + case APIResultValues.Tag.Info: return { tag: APIResultValues.Tag.Info }; + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); + } + } + }); return { /** @@ -200,8 +198,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -403,7 +400,7 @@ export async function createInstantiator(options, swift) { const callback = swift.memory.getObject(callbackId); const enumValue = enumHelpers.APIResult.lift(param0); let ret = callback(enumValue); - const { caseId: caseId, cleanup: cleanup } = enumHelpers.APIResult.lower(ret); + const { caseId: caseId } = enumHelpers.APIResult.lower(ret); return caseId; } catch (error) { setException(error); @@ -414,7 +411,7 @@ export async function createInstantiator(options, swift) { const { caseId: param0CaseId, cleanup: param0Cleanup } = enumHelpers.APIResult.lower(param0); instance.exports.invoke_swift_closure_TestModule_10TestModule9APIResultO_9APIResultO(boxPtr, param0CaseId); const ret = enumHelpers.APIResult.lift(i32Stack.pop()); - if (param0Cleanup) { param0Cleanup(); } + param0Cleanup && param0Cleanup(); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -595,21 +592,20 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq10HttpStatusO_Sq10HttpStatusO); } - bjs["invoke_js_callback_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO"] = function(callbackId, param0IsSome, param0WrappedValue) { + bjs["invoke_js_callback_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO"] = function(callbackId, param0IsSome, param0ObjectId) { try { const callback = swift.memory.getObject(callbackId); - let obj; + let optResult; if (param0IsSome) { - obj = swift.memory.getObject(param0WrappedValue); - swift.memory.release(param0WrappedValue); - } - let ret = callback(param0IsSome ? obj : null); - const isSome = ret != null; - if (isSome) { - tmpRetString = ret; + const param0ObjectIdObject = swift.memory.getObject(param0ObjectId); + swift.memory.release(param0ObjectId); + optResult = param0ObjectIdObject; } else { - tmpRetString = null; + optResult = null; } + let ret = callback(optResult); + const isSome = ret != null; + tmpRetString = isSome ? ret : null; } catch (error) { setException(error); } @@ -617,12 +613,17 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO = function(param0) { const isSome = param0 != null; - let param0Id, param0Bytes; + let bytes, length; if (isSome) { - param0Bytes = textEncoder.encode(param0); - param0Id = swift.memory.retain(param0Bytes); + const param0Bytes = textEncoder.encode(param0); + const param0Id = swift.memory.retain(param0Bytes); + bytes = param0Id; + length = param0Bytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.invoke_swift_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO(boxPtr, +isSome, isSome ? param0Id : 0, isSome ? param0Bytes.length : 0); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO(boxPtr, +isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; if (tmpRetException) { @@ -635,10 +636,10 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO); } - bjs["invoke_js_callback_TestModule_10TestModuleSq6PersonC_Sq6PersonC"] = function(callbackId, param0IsSome, param0WrappedValue) { + bjs["invoke_js_callback_TestModule_10TestModuleSq6PersonC_Sq6PersonC"] = function(callbackId, param0IsSome, param0Pointer) { try { const callback = swift.memory.getObject(callbackId); - let ret = callback(param0IsSome ? _exports['Person'].__construct(param0WrappedValue) : null); + let ret = callback(param0IsSome ? _exports['Person'].__construct(param0Pointer) : null); const isSome = ret != null; return isSome ? ret.pointer : 0; } catch (error) { @@ -648,10 +649,16 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC = function(param0) { const isSome = param0 != null; - instance.exports.invoke_swift_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC(boxPtr, +isSome, isSome ? param0.pointer : 0); - const pointer = tmpRetOptionalHeapObject; + let pointer; + if (isSome) { + pointer = param0.pointer; + } else { + pointer = 0; + } + instance.exports.invoke_swift_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC(boxPtr, +isSome, pointer); + const pointer1 = tmpRetOptionalHeapObject; tmpRetOptionalHeapObject = undefined; - const optResult = pointer === null ? null : _exports['Person'].__construct(pointer); + const optResult = pointer1 === null ? null : _exports['Person'].__construct(pointer1); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -662,17 +669,20 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq6PersonC_Sq6PersonC); } - bjs["invoke_js_callback_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO"] = function(callbackId, param0IsSome, param0WrappedValue) { + bjs["invoke_js_callback_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO"] = function(callbackId, param0IsSome, param0CaseId) { try { const callback = swift.memory.getObject(callbackId); - let enumValue; + let optResult; if (param0IsSome) { - enumValue = enumHelpers.APIResult.lift(param0WrappedValue); + const enumValue = enumHelpers.APIResult.lift(param0CaseId); + optResult = enumValue; + } else { + optResult = null; } - let ret = callback(param0IsSome ? enumValue : null); + let ret = callback(optResult); const isSome = ret != null; if (isSome) { - const { caseId: caseId, cleanup: cleanup } = enumHelpers.APIResult.lower(ret); + const { caseId: caseId } = enumHelpers.APIResult.lower(ret); return caseId; } else { return -1; @@ -684,22 +694,21 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO = function(param0) { const isSome = param0 != null; - let param0CaseId, param0Cleanup; + let caseId; + let param0Cleanup1; if (isSome) { - const enumResult = enumHelpers.APIResult.lower(param0); - param0CaseId = enumResult.caseId; - param0Cleanup = enumResult.cleanup; - } - instance.exports.invoke_swift_closure_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO(boxPtr, +isSome, isSome ? param0CaseId : 0); - const tag = i32Stack.pop(); - const isNull = (tag === -1); - let optResult; - if (isNull) { - optResult = null; + const { caseId: param0CaseId, cleanup: param0Cleanup } = enumHelpers.APIResult.lower(param0); + caseId = param0CaseId; + param0Cleanup1 = () => { + param0Cleanup && param0Cleanup(); + }; } else { - optResult = enumHelpers.APIResult.lift(tag); + caseId = 0; } - if (param0Cleanup) { param0Cleanup(); } + instance.exports.invoke_swift_closure_TestModule_10TestModuleSq9APIResultO_Sq9APIResultO(boxPtr, +isSome, caseId); + const tag = i32Stack.pop(); + const optResult = tag === -1 ? null : enumHelpers.APIResult.lift(tag); + param0Cleanup1 && param0Cleanup1(); if (tmpRetException) { const error = swift.memory.getObject(tmpRetException); swift.memory.release(tmpRetException); @@ -715,7 +724,7 @@ export async function createInstantiator(options, swift) { const callback = swift.memory.getObject(callbackId); let ret = callback(param0IsSome ? param0WrappedValue : null); const isSome = ret != null; - return isSome ? (ret | 0) : -1; + return isSome ? ret : -1; } catch (error) { setException(error); } @@ -736,21 +745,20 @@ export async function createInstantiator(options, swift) { }; return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq9DirectionO_Sq9DirectionO); } - bjs["invoke_js_callback_TestModule_10TestModuleSqSS_SqSS"] = function(callbackId, param0IsSome, param0WrappedValue) { + bjs["invoke_js_callback_TestModule_10TestModuleSqSS_SqSS"] = function(callbackId, param0IsSome, param0ObjectId) { try { const callback = swift.memory.getObject(callbackId); - let obj; + let optResult; if (param0IsSome) { - obj = swift.memory.getObject(param0WrappedValue); - swift.memory.release(param0WrappedValue); - } - let ret = callback(param0IsSome ? obj : null); - const isSome = ret != null; - if (isSome) { - tmpRetString = ret; + const param0ObjectIdObject = swift.memory.getObject(param0ObjectId); + swift.memory.release(param0ObjectId); + optResult = param0ObjectIdObject; } else { - tmpRetString = null; + optResult = null; } + let ret = callback(optResult); + const isSome = ret != null; + tmpRetString = isSome ? ret : null; } catch (error) { setException(error); } @@ -758,12 +766,17 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSqSS_SqSS"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSqSS_SqSS = function(param0) { const isSome = param0 != null; - let param0Id, param0Bytes; + let bytes, length; if (isSome) { - param0Bytes = textEncoder.encode(param0); - param0Id = swift.memory.retain(param0Bytes); + const param0Bytes = textEncoder.encode(param0); + const param0Id = swift.memory.retain(param0Bytes); + bytes = param0Id; + length = param0Bytes.length; + } else { + bytes = 0; + length = 0; } - instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSS_SqSS(boxPtr, +isSome, isSome ? param0Id : 0, isSome ? param0Bytes.length : 0); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSS_SqSS(boxPtr, +isSome, bytes, length); const optResult = tmpRetString; tmpRetString = undefined; if (tmpRetException) { @@ -789,7 +802,7 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSqSb_SqSb"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSqSb_SqSb = function(param0) { const isSome = param0 != null; - instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSb_SqSb(boxPtr, +isSome, isSome ? param0 : 0); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSb_SqSb(boxPtr, +isSome, isSome ? param0 ? 1 : 0 : 0); const optResult = tmpRetOptionalBool; tmpRetOptionalBool = undefined; if (tmpRetException) { @@ -815,7 +828,7 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSqSd_SqSd"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSqSd_SqSd = function(param0) { const isSome = param0 != null; - instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSd_SqSd(boxPtr, +isSome, isSome ? param0 : 0); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSd_SqSd(boxPtr, +isSome, isSome ? param0 : 0.0); const optResult = tmpRetOptionalDouble; tmpRetOptionalDouble = undefined; if (tmpRetException) { @@ -841,7 +854,7 @@ export async function createInstantiator(options, swift) { bjs["make_swift_closure_TestModule_10TestModuleSqSf_SqSf"] = function(boxPtr, file, line) { const lower_closure_TestModule_10TestModuleSqSf_SqSf = function(param0) { const isSome = param0 != null; - instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSf_SqSf(boxPtr, +isSome, isSome ? param0 : 0); + instance.exports.invoke_swift_closure_TestModule_10TestModuleSqSf_SqSf(boxPtr, +isSome, isSome ? param0 : 0.0); const optResult = tmpRetOptionalFloat; tmpRetOptionalFloat = undefined; if (tmpRetException) { @@ -946,7 +959,7 @@ export async function createInstantiator(options, swift) { return TestProcessor.__construct(ret); } } - const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers(); enumHelpers.APIResult = APIResultHelpers; const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js index 61347e817..028797aa2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js @@ -102,8 +102,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js index 316dfb709..14f69fa5f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js @@ -34,236 +34,242 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createDataPointHelpers = () => { - return () => ({ - lower: (value) => { - f64Stack.push(value.x); - f64Stack.push(value.y); - const bytes = textEncoder.encode(value.label); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const isSome = value.optCount != null; - if (isSome) { - i32Stack.push(value.optCount | 0); - } else { - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const isSome1 = value.optFlag != null; - if (isSome1) { - i32Stack.push(value.optFlag ? 1 : 0); - } else { - i32Stack.push(0); - } - i32Stack.push(isSome1 ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const bool = i32Stack.pop() !== 0; - optional = bool; - } else { - optional = null; - } - const isSome1 = i32Stack.pop(); - let optional1; - if (isSome1) { - const int = i32Stack.pop(); - optional1 = int; - } else { - optional1 = null; - } - const string = strStack.pop(); - const f64 = f64Stack.pop(); - const f641 = f64Stack.pop(); - return { x: f641, y: f64, label: string, optCount: optional1, optFlag: optional }; - } - }); - }; - const __bjs_createAddressHelpers = () => { - return () => ({ - lower: (value) => { - const bytes = textEncoder.encode(value.street); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - const bytes1 = textEncoder.encode(value.city); + const __bjs_createDataPointHelpers = () => ({ + lower: (value) => { + f64Stack.push(value.x); + f64Stack.push(value.y); + const bytes = textEncoder.encode(value.label); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const isSome = value.optCount != null; + if (isSome) { + i32Stack.push((value.optCount | 0)); + } else { + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + const isSome1 = value.optFlag != null; + if (isSome1) { + i32Stack.push(value.optFlag ? 1 : 0); + } else { + i32Stack.push(0); + } + i32Stack.push(isSome1 ? 1 : 0); + return { cleanup: undefined }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const bool = i32Stack.pop() !== 0; + optional = bool; + } else { + optional = null; + } + const isSome1 = i32Stack.pop(); + let optional1; + if (isSome1) { + const int = i32Stack.pop(); + optional1 = int; + } else { + optional1 = null; + } + const string = strStack.pop(); + const f64 = f64Stack.pop(); + const f641 = f64Stack.pop(); + return { x: f641, y: f64, label: string, optCount: optional1, optFlag: optional }; + } + }); + const __bjs_createAddressHelpers = () => ({ + lower: (value) => { + const bytes = textEncoder.encode(value.street); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + const bytes1 = textEncoder.encode(value.city); + const id1 = swift.memory.retain(bytes1); + i32Stack.push(bytes1.length); + i32Stack.push(id1); + const isSome = value.zipCode != null; + if (isSome) { + i32Stack.push((value.zipCode | 0)); + } else { + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + return { cleanup: undefined }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const int = i32Stack.pop(); + optional = int; + } else { + optional = null; + } + const string = strStack.pop(); + const string1 = strStack.pop(); + return { street: string1, city: string, zipCode: optional }; + } + }); + const __bjs_createPersonHelpers = () => ({ + lower: (value) => { + const bytes = textEncoder.encode(value.name); + const id = swift.memory.retain(bytes); + i32Stack.push(bytes.length); + i32Stack.push(id); + i32Stack.push((value.age | 0)); + const structResult = structHelpers.Address.lower(value.address); + const isSome = value.email != null; + if (isSome) { + const bytes1 = textEncoder.encode(value.email); const id1 = swift.memory.retain(bytes1); i32Stack.push(bytes1.length); i32Stack.push(id1); - const isSome = value.zipCode != null; - if (isSome) { - i32Stack.push(value.zipCode | 0); - } else { - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const int = i32Stack.pop(); - optional = int; - } else { - optional = null; - } + } else { + i32Stack.push(0); + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + structResult.cleanup && structResult.cleanup(); + }; + return { cleanup }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { const string = strStack.pop(); - const string1 = strStack.pop(); - return { street: string1, city: string, zipCode: optional }; - } - }); - }; - const __bjs_createPersonHelpers = () => { - return () => ({ - lower: (value) => { - const bytes = textEncoder.encode(value.name); - const id = swift.memory.retain(bytes); - i32Stack.push(bytes.length); - i32Stack.push(id); - i32Stack.push((value.age | 0)); - const structResult = structHelpers.Address.lower(value.address); - const isSome = value.email != null; - let id1; - if (isSome) { - const bytes1 = textEncoder.encode(value.email); - id1 = swift.memory.retain(bytes1); - i32Stack.push(bytes1.length); - i32Stack.push(id1); - } else { - i32Stack.push(0); - i32Stack.push(0); - } - i32Stack.push(isSome ? 1 : 0); - const cleanup = () => { - if (structResult.cleanup) { structResult.cleanup(); } - }; - return { cleanup }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const string = strStack.pop(); - optional = string; - } else { - optional = null; - } - const struct = structHelpers.Address.lift(); - const int = i32Stack.pop(); - const string1 = strStack.pop(); - return { name: string1, age: int, address: struct, email: optional }; - } - }); - }; - const __bjs_createSessionHelpers = () => { - return () => ({ - lower: (value) => { - i32Stack.push((value.id | 0)); - ptrStack.push(value.owner.pointer); - return { cleanup: undefined }; - }, - lift: () => { - const ptr = ptrStack.pop(); - const obj = _exports['Greeter'].__construct(ptr); - const int = i32Stack.pop(); - return { id: int, owner: obj }; - } - }); - }; - const __bjs_createMeasurementHelpers = () => { - return () => ({ - lower: (value) => { - f64Stack.push(value.value); - f32Stack.push(Math.fround(value.precision)); - const isSome = value.optionalPrecision != null; - if (isSome) { - f32Stack.push(Math.fround(value.optionalPrecision)); - } else { - f32Stack.push(0.0); - } - i32Stack.push(isSome ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const rawValue = f32Stack.pop(); - optional = rawValue; - } else { - optional = null; - } - const rawValue1 = f32Stack.pop(); - const f64 = f64Stack.pop(); - return { value: f64, precision: rawValue1, optionalPrecision: optional }; - } - }); - }; - const __bjs_createConfigStructHelpers = () => { - return () => ({ - lower: (value) => { - return { cleanup: undefined }; - }, - lift: () => { - return { }; - } - }); - }; - const __bjs_createContainerHelpers = () => { - return () => ({ - lower: (value) => { - let id; - if (value.object != null) { - id = swift.memory.retain(value.object); - } else { - id = undefined; - } - i32Stack.push(id !== undefined ? id : 0); - const isSome = value.optionalObject != null; + optional = string; + } else { + optional = null; + } + const struct = structHelpers.Address.lift(); + const int = i32Stack.pop(); + const string1 = strStack.pop(); + return { name: string1, age: int, address: struct, email: optional }; + } + }); + const __bjs_createSessionHelpers = () => ({ + lower: (value) => { + i32Stack.push((value.id | 0)); + ptrStack.push(value.owner.pointer); + return { cleanup: undefined }; + }, + lift: () => { + const ptr = ptrStack.pop(); + const obj = _exports['Greeter'].__construct(ptr); + const int = i32Stack.pop(); + return { id: int, owner: obj }; + } + }); + const __bjs_createMeasurementHelpers = () => ({ + lower: (value) => { + f64Stack.push(value.value); + f32Stack.push(Math.fround(value.precision)); + const isSome = value.optionalPrecision != null; + if (isSome) { + f32Stack.push(Math.fround(value.optionalPrecision)); + } else { + f32Stack.push(0.0); + } + i32Stack.push(isSome ? 1 : 0); + return { cleanup: undefined }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const rawValue = f32Stack.pop(); + optional = rawValue; + } else { + optional = null; + } + const rawValue1 = f32Stack.pop(); + const f64 = f64Stack.pop(); + return { value: f64, precision: rawValue1, optionalPrecision: optional }; + } + }); + const __bjs_createConfigStructHelpers = () => ({ + lower: (value) => { + return { cleanup: undefined }; + }, + lift: () => { + return { }; + } + }); + const __bjs_createContainerHelpers = () => ({ + lower: (value) => { + let id; + if (value.object != null) { + id = swift.memory.retain(value.object); + } else { + id = undefined; + } + i32Stack.push(id !== undefined ? id : 0); + const isSome = value.optionalObject != null; + let innerCleanup; + if (isSome) { let id1; - if (isSome) { + if (value.optionalObject != null) { id1 = swift.memory.retain(value.optionalObject); - i32Stack.push(id1); } else { id1 = undefined; - i32Stack.push(0); } - i32Stack.push(isSome ? 1 : 0); - return { cleanup: undefined }; - }, - lift: () => { - const isSome = i32Stack.pop(); - let optional; - if (isSome) { - const objectId = i32Stack.pop(); - let value; - if (objectId !== 0) { - value = swift.memory.getObject(objectId); - swift.memory.release(objectId); - } else { - value = null; + i32Stack.push(id1 !== undefined ? id1 : 0); + innerCleanup = () => { + if(id1 !== undefined && id1 !== 0) { + try { + swift.memory.getObject(id1); + swift.memory.release(id1); + } catch(e) { console.warn('BridgeJS: cleanup failed for retained object', e); } } - optional = value; - } else { - optional = null; - } - const objectId1 = i32Stack.pop(); - let value1; - if (objectId1 !== 0) { - value1 = swift.memory.getObject(objectId1); - swift.memory.release(objectId1); + }; + } else { + i32Stack.push(0); + } + i32Stack.push(isSome ? 1 : 0); + const cleanup = () => { + if(id !== undefined && id !== 0) { + try { + swift.memory.getObject(id); + swift.memory.release(id); + } catch(e) { console.warn('BridgeJS: cleanup failed for retained object', e); } + } + innerCleanup && innerCleanup(); + }; + return { cleanup }; + }, + lift: () => { + const isSome = i32Stack.pop(); + let optional; + if (isSome) { + const objectId = i32Stack.pop(); + let value; + if (objectId !== 0) { + value = swift.memory.getObject(objectId); + swift.memory.release(objectId); } else { - value1 = null; - } - return { object: value1, optionalObject: optional }; - } - }); - }; + value = null; + } + optional = value; + } else { + optional = null; + } + const objectId1 = i32Stack.pop(); + let value1; + if (objectId1 !== 0) { + value1 = swift.memory.getObject(objectId1); + swift.memory.release(objectId1); + } else { + value1 = null; + } + return { object: value1, optionalObject: optional }; + } + }); return { /** @@ -311,8 +317,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -575,25 +580,25 @@ export async function createInstantiator(options, swift) { instance.exports.bjs_Greeter_name_set(this.pointer, valueId, valueBytes.length); } } - const DataPointHelpers = __bjs_createDataPointHelpers()(); + const DataPointHelpers = __bjs_createDataPointHelpers(); structHelpers.DataPoint = DataPointHelpers; - const AddressHelpers = __bjs_createAddressHelpers()(); + const AddressHelpers = __bjs_createAddressHelpers(); structHelpers.Address = AddressHelpers; - const PersonHelpers = __bjs_createPersonHelpers()(); + const PersonHelpers = __bjs_createPersonHelpers(); structHelpers.Person = PersonHelpers; - const SessionHelpers = __bjs_createSessionHelpers()(); + const SessionHelpers = __bjs_createSessionHelpers(); structHelpers.Session = SessionHelpers; - const MeasurementHelpers = __bjs_createMeasurementHelpers()(); + const MeasurementHelpers = __bjs_createMeasurementHelpers(); structHelpers.Measurement = MeasurementHelpers; - const ConfigStructHelpers = __bjs_createConfigStructHelpers()(); + const ConfigStructHelpers = __bjs_createConfigStructHelpers(); structHelpers.ConfigStruct = ConfigStructHelpers; - const ContainerHelpers = __bjs_createContainerHelpers()(); + const ContainerHelpers = __bjs_createContainerHelpers(); structHelpers.Container = ContainerHelpers; const exports = { @@ -619,7 +624,7 @@ export async function createInstantiator(options, swift) { const labelId = swift.memory.retain(labelBytes); const isSome = optCount != null; const isSome1 = optFlag != null; - instance.exports.bjs_DataPoint_init(x, y, labelId, labelBytes.length, +isSome, isSome ? optCount : 0, +isSome1, isSome1 ? optFlag : 0); + instance.exports.bjs_DataPoint_init(x, y, labelId, labelBytes.length, +isSome, isSome ? optCount : 0, +isSome1, isSome1 ? optFlag ? 1 : 0 : 0); const structValue = structHelpers.DataPoint.lift(); return structValue; }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js index 4962e659b..e6a6640f4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js @@ -29,20 +29,18 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createPointHelpers = () => { - return () => ({ - lower: (value) => { - i32Stack.push((value.x | 0)); - i32Stack.push((value.y | 0)); - return { cleanup: undefined }; - }, - lift: () => { - const int = i32Stack.pop(); - const int1 = i32Stack.pop(); - return { x: int1, y: int }; - } - }); - }; + const __bjs_createPointHelpers = () => ({ + lower: (value) => { + i32Stack.push((value.x | 0)); + i32Stack.push((value.y | 0)); + return { cleanup: undefined }; + }, + lift: () => { + const int = i32Stack.pop(); + const int1 = i32Stack.pop(); + return { x: int1, y: int }; + } + }); return { /** @@ -91,8 +89,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -244,7 +241,7 @@ export async function createInstantiator(options, swift) { /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { const js = swift.memory.heap; - const PointHelpers = __bjs_createPointHelpers()(); + const PointHelpers = __bjs_createPointHelpers(); structHelpers.Point = PointHelpers; const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js index 3726a48ae..358b4c77c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Throws.js @@ -76,8 +76,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js index 7c3627d13..fe340b3bb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.js @@ -29,26 +29,24 @@ export async function createInstantiator(options, swift) { let _exports = null; let bjs = null; - const __bjs_createPointerFieldsHelpers = () => { - return () => ({ - lower: (value) => { - ptrStack.push((value.raw | 0)); - ptrStack.push((value.mutRaw | 0)); - ptrStack.push((value.opaque | 0)); - ptrStack.push((value.ptr | 0)); - ptrStack.push((value.mutPtr | 0)); - return { cleanup: undefined }; - }, - lift: () => { - const pointer = ptrStack.pop(); - const pointer1 = ptrStack.pop(); - const pointer2 = ptrStack.pop(); - const pointer3 = ptrStack.pop(); - const pointer4 = ptrStack.pop(); - return { raw: pointer4, mutRaw: pointer3, opaque: pointer2, ptr: pointer1, mutPtr: pointer }; - } - }); - }; + const __bjs_createPointerFieldsHelpers = () => ({ + lower: (value) => { + ptrStack.push((value.raw | 0)); + ptrStack.push((value.mutRaw | 0)); + ptrStack.push((value.opaque | 0)); + ptrStack.push((value.ptr | 0)); + ptrStack.push((value.mutPtr | 0)); + return { cleanup: undefined }; + }, + lift: () => { + const pointer = ptrStack.pop(); + const pointer1 = ptrStack.pop(); + const pointer2 = ptrStack.pop(); + const pointer3 = ptrStack.pop(); + const pointer4 = ptrStack.pop(); + return { raw: pointer4, mutRaw: pointer3, opaque: pointer2, ptr: pointer1, mutPtr: pointer }; + } + }); return { /** @@ -96,8 +94,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); @@ -238,7 +235,7 @@ export async function createInstantiator(options, swift) { /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { const js = swift.memory.heap; - const PointerFieldsHelpers = __bjs_createPointerFieldsHelpers()(); + const PointerFieldsHelpers = __bjs_createPointerFieldsHelpers(); structHelpers.PointerFields = PointerFieldsHelpers; const exports = { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js index e338c840d..95f4a644d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.js @@ -77,8 +77,7 @@ export async function createInstantiator(options, swift) { } bjs["swift_js_push_string"] = function(ptr, len) { const bytes = new Uint8Array(memory.buffer, ptr, len); - const value = textDecoder.decode(bytes); - strStack.push(value); + strStack.push(textDecoder.decode(bytes)); } bjs["swift_js_pop_i32"] = function() { return i32Stack.pop(); diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 96ca9be2e..513de9175 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -1026,10 +1026,10 @@ extension JSTypedClosure where Signature == (Optional) -> String { @_expose(wasm, "invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") @_cdecl("invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS") -public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Value: UnsafeMutableRawPointer) -> Void { +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_SS(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> String>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Pointer)) return result.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -1083,10 +1083,10 @@ extension JSTypedClosure where Signature == (Optional) -> Optional Void { +public func _invoke_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC(_ boxPtr: UnsafeMutableRawPointer, _ param0IsSome: Int32, _ param0Pointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) let closure = Unmanaged<_BridgeJSTypedClosureBox<(Optional) -> Optional>>.fromOpaque(boxPtr).takeUnretainedValue().closure - let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Value)) + let result = closure(Optional.bridgeJSLiftParameter(param0IsSome, param0Pointer)) return result.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5912,9 +5912,9 @@ public func _bjs_roundTripOptionalPayloadResultOpt(_ resultIsSome: Int32, _ resu @_expose(wasm, "bjs_roundTripOptionalClass") @_cdecl("bjs_roundTripOptionalClass") -public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + let ret = roundTripOptionalClass(value: Optional.bridgeJSLiftParameter(valueIsSome, valuePointer)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5923,9 +5923,9 @@ public func _bjs_roundTripOptionalClass(_ valueIsSome: Int32, _ valueValue: Unsa @_expose(wasm, "bjs_roundTripOptionalGreeter") @_cdecl("bjs_roundTripOptionalGreeter") -public func _bjs_roundTripOptionalGreeter(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_roundTripOptionalGreeter(_ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let ret = roundTripOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valueValue)) + let ret = roundTripOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valuePointer)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5934,9 +5934,9 @@ public func _bjs_roundTripOptionalGreeter(_ valueIsSome: Int32, _ valueValue: Un @_expose(wasm, "bjs_applyOptionalGreeter") @_cdecl("bjs_applyOptionalGreeter") -public func _bjs_applyOptionalGreeter(_ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer, _ transform: Int32) -> Void { +public func _bjs_applyOptionalGreeter(_ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer, _ transform: Int32) -> Void { #if arch(wasm32) - let ret = applyOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valueValue), _: _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC.bridgeJSLift(transform)) + let ret = applyOptionalGreeter(_: Optional.bridgeJSLiftParameter(valueIsSome, valuePointer), _: _BJS_Closure_20BridgeJSRuntimeTestsSq7GreeterC_Sq7GreeterC.bridgeJSLift(transform)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -5945,9 +5945,9 @@ public func _bjs_applyOptionalGreeter(_ valueIsSome: Int32, _ valueValue: Unsafe @_expose(wasm, "bjs_makeOptionalHolder") @_cdecl("bjs_makeOptionalHolder") -public func _bjs_makeOptionalHolder(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { +public func _bjs_makeOptionalHolder(_ nullableGreeterIsSome: Int32, _ nullableGreeterPointer: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { #if arch(wasm32) - let ret = makeOptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) + let ret = makeOptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterPointer), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -7717,7 +7717,7 @@ public func _bjs_DataProcessorManager_setProcessorAPIResult(_ _self: UnsafeMutab @_cdecl("bjs_DataProcessorManager_processor_get") public func _bjs_DataProcessorManager_processor_get(_ _self: UnsafeMutableRawPointer) -> Int32 { #if arch(wasm32) - let ret = DataProcessorManager.bridgeJSLiftParameter(_self).processor as! AnyDataProcessor + let ret = (DataProcessorManager.bridgeJSLiftParameter(_self).processor as! AnyDataProcessor) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -7868,9 +7868,9 @@ public func _bjs_SwiftDataProcessor_createGreeter(_ _self: UnsafeMutableRawPoint @_expose(wasm, "bjs_SwiftDataProcessor_processOptionalGreeter") @_cdecl("bjs_SwiftDataProcessor_processOptionalGreeter") -public func _bjs_SwiftDataProcessor_processOptionalGreeter(_ _self: UnsafeMutableRawPointer, _ greeterIsSome: Int32, _ greeterValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_SwiftDataProcessor_processOptionalGreeter(_ _self: UnsafeMutableRawPointer, _ greeterIsSome: Int32, _ greeterPointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - let ret = SwiftDataProcessor.bridgeJSLiftParameter(_self).processOptionalGreeter(_: Optional.bridgeJSLiftParameter(greeterIsSome, greeterValue)) + let ret = SwiftDataProcessor.bridgeJSLiftParameter(_self).processOptionalGreeter(_: Optional.bridgeJSLiftParameter(greeterIsSome, greeterPointer)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -8101,9 +8101,9 @@ public func _bjs_SwiftDataProcessor_optionalHelper_get(_ _self: UnsafeMutableRaw @_expose(wasm, "bjs_SwiftDataProcessor_optionalHelper_set") @_cdecl("bjs_SwiftDataProcessor_optionalHelper_set") -public func _bjs_SwiftDataProcessor_optionalHelper_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_SwiftDataProcessor_optionalHelper_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - SwiftDataProcessor.bridgeJSLiftParameter(_self).optionalHelper = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + SwiftDataProcessor.bridgeJSLiftParameter(_self).optionalHelper = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif @@ -8392,9 +8392,9 @@ fileprivate func _bjs_TextProcessor_wrap(_ pointer: UnsafeMutableRawPointer) -> @_expose(wasm, "bjs_OptionalHolder_init") @_cdecl("bjs_OptionalHolder_init") -public func _bjs_OptionalHolder_init(_ nullableGreeterIsSome: Int32, _ nullableGreeterValue: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { +public func _bjs_OptionalHolder_init(_ nullableGreeterIsSome: Int32, _ nullableGreeterPointer: UnsafeMutableRawPointer, _ undefinedNumberIsSome: Int32, _ undefinedNumberValue: Float64) -> UnsafeMutableRawPointer { #if arch(wasm32) - let ret = OptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterValue), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) + let ret = OptionalHolder(nullableGreeter: Optional.bridgeJSLiftParameter(nullableGreeterIsSome, nullableGreeterPointer), undefinedNumber: JSUndefinedOr.bridgeJSLiftParameter(undefinedNumberIsSome, undefinedNumberValue)) return ret.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") @@ -8414,9 +8414,9 @@ public func _bjs_OptionalHolder_nullableGreeter_get(_ _self: UnsafeMutableRawPoi @_expose(wasm, "bjs_OptionalHolder_nullableGreeter_set") @_cdecl("bjs_OptionalHolder_nullableGreeter_set") -public func _bjs_OptionalHolder_nullableGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_OptionalHolder_nullableGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - OptionalHolder.bridgeJSLiftParameter(_self).nullableGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + OptionalHolder.bridgeJSLiftParameter(_self).nullableGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif @@ -8534,9 +8534,9 @@ public func _bjs_OptionalPropertyHolder_optionalGreeter_get(_ _self: UnsafeMutab @_expose(wasm, "bjs_OptionalPropertyHolder_optionalGreeter_set") @_cdecl("bjs_OptionalPropertyHolder_optionalGreeter_set") -public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valueValue: UnsafeMutableRawPointer) -> Void { +public func _bjs_OptionalPropertyHolder_optionalGreeter_set(_ _self: UnsafeMutableRawPointer, _ valueIsSome: Int32, _ valuePointer: UnsafeMutableRawPointer) -> Void { #if arch(wasm32) - OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + OptionalPropertyHolder.bridgeJSLiftParameter(_self).optionalGreeter = Optional.bridgeJSLiftParameter(valueIsSome, valuePointer) #else fatalError("Only available on WebAssembly") #endif diff --git a/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs b/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs index 58215560c..6a02eb180 100644 --- a/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs +++ b/Tests/BridgeJSRuntimeTests/JavaScript/OptionalSupportTests.mjs @@ -1,6 +1,6 @@ // @ts-check -import assert from 'node:assert'; +import assert from "node:assert"; import { StatusValues, ThemeValues, @@ -11,9 +11,9 @@ import { APIOptionalResultValues, AllTypesResultValues, OptionalAllTypesResultValues, -} from '../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; +} from "../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js"; -import { ImportedFoo } from './Types.mjs'; +import { ImportedFoo } from "./Types.mjs"; /** * @returns {import('../../../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Imports["OptionalSupportImports"]} @@ -34,7 +34,9 @@ export function getImports(importsContext) { }, runJsOptionalSupportTests: () => { const exports = importsContext.getExports(); - if (!exports) { throw new Error("No exports!?"); } + if (!exports) { + throw new Error("No exports!?"); + } runJsOptionalSupportTests(exports); }, }; @@ -51,50 +53,87 @@ export function runJsOptionalSupportTests(exports) { assert.equal(exports.roundTripOptionalFloat(null), null); assert.equal(exports.roundTripOptionalDouble(null), null); - assert.equal(exports.roundTripOptionalString('Hello'), 'Hello'); + assert.equal(exports.roundTripOptionalString("Hello"), "Hello"); assert.equal(exports.roundTripOptionalInt(42), 42); assert.equal(exports.roundTripOptionalBool(true), true); - assert.equal(exports.roundTripOptionalFloat(3.141592502593994), 3.141592502593994); // Float32 precision + assert.equal( + exports.roundTripOptionalFloat(3.141592502593994), + 3.141592502593994, + ); // Float32 precision assert.equal(exports.roundTripOptionalDouble(2.718), 2.718); assert.equal(exports.roundTripOptionalSyntax(null), null); - assert.equal(exports.roundTripOptionalSyntax('Test'), 'Test'); + assert.equal(exports.roundTripOptionalSyntax("Test"), "Test"); assert.equal(exports.roundTripOptionalMixSyntax(null), null); - assert.equal(exports.roundTripOptionalMixSyntax('Mix'), 'Mix'); + assert.equal(exports.roundTripOptionalMixSyntax("Mix"), "Mix"); assert.equal(exports.roundTripOptionalSwiftSyntax(null), null); - assert.equal(exports.roundTripOptionalSwiftSyntax('Swift'), 'Swift'); + assert.equal(exports.roundTripOptionalSwiftSyntax("Swift"), "Swift"); assert.equal(exports.roundTripOptionalWithSpaces(null), null); assert.equal(exports.roundTripOptionalWithSpaces(1.618), 1.618); assert.equal(exports.roundTripOptionalTypeAlias(null), null); assert.equal(exports.roundTripOptionalTypeAlias(25), 25); - assert.equal(exports.roundTripOptionalStatus(exports.Status.Success), StatusValues.Success); - assert.equal(exports.roundTripOptionalTheme(exports.Theme.Light), ThemeValues.Light); - assert.equal(exports.roundTripOptionalHttpStatus(exports.HttpStatus.Ok), HttpStatusValues.Ok); - assert.equal(exports.roundTripOptionalTSDirection(TSDirection.North), TSDirection.North); - assert.equal(exports.roundTripOptionalTSTheme(TSTheme.Light), TSTheme.Light); - assert.equal(exports.roundTripOptionalNetworkingAPIMethod(exports.Networking.API.Method.Get), exports.Networking.API.Method.Get); + assert.equal( + exports.roundTripOptionalStatus(exports.Status.Success), + StatusValues.Success, + ); + assert.equal( + exports.roundTripOptionalTheme(exports.Theme.Light), + ThemeValues.Light, + ); + assert.equal( + exports.roundTripOptionalHttpStatus(exports.HttpStatus.Ok), + HttpStatusValues.Ok, + ); + assert.equal( + exports.roundTripOptionalTSDirection(TSDirection.North), + TSDirection.North, + ); + assert.equal( + exports.roundTripOptionalTSTheme(TSTheme.Light), + TSTheme.Light, + ); + assert.equal( + exports.roundTripOptionalNetworkingAPIMethod( + exports.Networking.API.Method.Get, + ), + exports.Networking.API.Method.Get, + ); const pVal = 3.141592653589793; const p1 = { tag: APIResultValues.Tag.Precise, param0: pVal }; - const cl1 = { tag: exports.ComplexResult.Tag.Location, param0: 37.7749, param1: -122.4194, param2: 'San Francisco' }; + const cl1 = { + tag: exports.ComplexResult.Tag.Location, + param0: 37.7749, + param1: -122.4194, + param2: "San Francisco", + }; assert.deepEqual(exports.roundTripOptionalAPIResult(p1), p1); assert.deepEqual(exports.roundTripOptionalComplexResult(cl1), cl1); - const apiSuccess = { tag: exports.APIResult.Tag.Success, param0: 'test success' }; + const apiSuccess = { + tag: exports.APIResult.Tag.Success, + param0: "test success", + }; const apiFailure = { tag: exports.APIResult.Tag.Failure, param0: 404 }; const apiInfo = { tag: exports.APIResult.Tag.Info }; - assert.equal(exports.compareAPIResults(apiSuccess, apiFailure), 'r1:success:test success,r2:failure:404'); - assert.equal(exports.compareAPIResults(null, apiInfo), 'r1:nil,r2:info'); - assert.equal(exports.compareAPIResults(apiFailure, null), 'r1:failure:404,r2:nil'); - assert.equal(exports.compareAPIResults(null, null), 'r1:nil,r2:nil'); - - const optionalGreeter = new exports.Greeter('Schrödinger'); + assert.equal( + exports.compareAPIResults(apiSuccess, apiFailure), + "r1:success:test success,r2:failure:404", + ); + assert.equal(exports.compareAPIResults(null, apiInfo), "r1:nil,r2:info"); + assert.equal( + exports.compareAPIResults(apiFailure, null), + "r1:failure:404,r2:nil", + ); + assert.equal(exports.compareAPIResults(null, null), "r1:nil,r2:nil"); + + const optionalGreeter = new exports.Greeter("Schrödinger"); const optionalGreeter2 = exports.roundTripOptionalClass(optionalGreeter); - assert.equal(optionalGreeter2?.greet() ?? '', 'Hello, Schrödinger!'); - assert.equal(optionalGreeter2?.name ?? '', 'Schrödinger'); - assert.equal(optionalGreeter2?.prefix ?? '', 'Hello'); + assert.equal(optionalGreeter2?.greet() ?? "", "Hello, Schrödinger!"); + assert.equal(optionalGreeter2?.name ?? "", "Schrödinger"); + assert.equal(optionalGreeter2?.prefix ?? "", "Hello"); assert.equal(exports.roundTripOptionalClass(null), null); optionalGreeter.release(); optionalGreeter2?.release(); @@ -105,15 +144,15 @@ export function runJsOptionalSupportTests(exports) { assert.equal(optionalsHolder.optionalAge, null); assert.equal(optionalsHolder.optionalGreeter, null); - optionalsHolder.optionalName = 'Alice'; + optionalsHolder.optionalName = "Alice"; optionalsHolder.optionalAge = 25; - assert.equal(optionalsHolder.optionalName, 'Alice'); + assert.equal(optionalsHolder.optionalName, "Alice"); assert.equal(optionalsHolder.optionalAge, 25); - const testPropertyGreeter = new exports.Greeter('Bob'); + const testPropertyGreeter = new exports.Greeter("Bob"); optionalsHolder.optionalGreeter = testPropertyGreeter; - assert.equal(optionalsHolder.optionalGreeter.greet(), 'Hello, Bob!'); - assert.equal(optionalsHolder.optionalGreeter.name, 'Bob'); + assert.equal(optionalsHolder.optionalGreeter.greet(), "Hello, Bob!"); + assert.equal(optionalsHolder.optionalGreeter.name, "Bob"); optionalsHolder.optionalName = null; optionalsHolder.optionalAge = null; @@ -124,36 +163,79 @@ export function runJsOptionalSupportTests(exports) { testPropertyGreeter.release(); optionalsHolder.release(); - const optGreeter = new exports.Greeter('Optionaly'); + 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!'); + 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 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.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 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 }; - const aor4 = { tag: APIOptionalResultValues.Tag.Failure, param0: 404, param1: null }; - const aor5 = { tag: APIOptionalResultValues.Tag.Failure, param0: null, param1: null }; - const aor6 = { tag: APIOptionalResultValues.Tag.Status, param0: true, param1: 200, param2: 'OK' }; - const aor7 = { tag: APIOptionalResultValues.Tag.Status, param0: true, param1: null, param2: 'Partial' }; - const aor8 = { tag: APIOptionalResultValues.Tag.Status, param0: null, param1: null, param2: 'Zero' }; - const aor9 = { tag: APIOptionalResultValues.Tag.Status, param0: false, param1: 500, param2: null }; - const aor10 = { tag: APIOptionalResultValues.Tag.Status, param0: null, param1: 0, param2: 'Zero' }; + const aor3 = { + tag: APIOptionalResultValues.Tag.Failure, + param0: 404, + param1: true, + }; + const aor4 = { + tag: APIOptionalResultValues.Tag.Failure, + param0: 404, + param1: null, + }; + const aor5 = { + tag: APIOptionalResultValues.Tag.Failure, + param0: null, + param1: null, + }; + const aor6 = { + tag: APIOptionalResultValues.Tag.Status, + param0: true, + param1: 200, + param2: "OK", + }; + const aor7 = { + tag: APIOptionalResultValues.Tag.Status, + param0: true, + param1: null, + param2: "Partial", + }; + const aor8 = { + tag: APIOptionalResultValues.Tag.Status, + param0: null, + param1: null, + param2: "Zero", + }; + const aor9 = { + tag: APIOptionalResultValues.Tag.Status, + param0: false, + param1: 500, + param2: null, + }; + const aor10 = { + tag: APIOptionalResultValues.Tag.Status, + param0: null, + param1: 0, + param2: "Zero", + }; assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor1), aor1); assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor2), aor2); @@ -168,77 +250,206 @@ export function runJsOptionalSupportTests(exports) { assert.equal(exports.roundTripOptionalAPIOptionalResult(null), null); // Optional TypedPayloadResult roundtrip - const tpr_precision = { tag: exports.TypedPayloadResult.Tag.Precision, param0: Math.fround(0.1) }; - assert.deepEqual(exports.roundTripOptionalTypedPayloadResult(tpr_precision), tpr_precision); - const tpr_direction = { tag: exports.TypedPayloadResult.Tag.Direction, param0: exports.Direction.North }; - assert.deepEqual(exports.roundTripOptionalTypedPayloadResult(tpr_direction), tpr_direction); - const tpr_optPrecisionSome = { tag: exports.TypedPayloadResult.Tag.OptPrecision, param0: Math.fround(0.001) }; - assert.deepEqual(exports.roundTripOptionalTypedPayloadResult(tpr_optPrecisionSome), tpr_optPrecisionSome); - const tpr_optPrecisionNull = { tag: exports.TypedPayloadResult.Tag.OptPrecision, param0: null }; - assert.deepEqual(exports.roundTripOptionalTypedPayloadResult(tpr_optPrecisionNull), tpr_optPrecisionNull); + const tpr_precision = { + tag: exports.TypedPayloadResult.Tag.Precision, + param0: Math.fround(0.1), + }; + assert.deepEqual( + exports.roundTripOptionalTypedPayloadResult(tpr_precision), + tpr_precision, + ); + const tpr_direction = { + tag: exports.TypedPayloadResult.Tag.Direction, + param0: exports.Direction.North, + }; + assert.deepEqual( + exports.roundTripOptionalTypedPayloadResult(tpr_direction), + tpr_direction, + ); + const tpr_optPrecisionSome = { + tag: exports.TypedPayloadResult.Tag.OptPrecision, + param0: Math.fround(0.001), + }; + assert.deepEqual( + exports.roundTripOptionalTypedPayloadResult(tpr_optPrecisionSome), + tpr_optPrecisionSome, + ); + const tpr_optPrecisionNull = { + tag: exports.TypedPayloadResult.Tag.OptPrecision, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalTypedPayloadResult(tpr_optPrecisionNull), + tpr_optPrecisionNull, + ); const tpr_empty = { tag: exports.TypedPayloadResult.Tag.Empty }; - assert.deepEqual(exports.roundTripOptionalTypedPayloadResult(tpr_empty), tpr_empty); + assert.deepEqual( + exports.roundTripOptionalTypedPayloadResult(tpr_empty), + tpr_empty, + ); assert.equal(exports.roundTripOptionalTypedPayloadResult(null), null); // Optional AllTypesResult roundtrip - const atr_struct = { tag: AllTypesResultValues.Tag.StructPayload, param0: { street: "100 Main St", city: "Boston", zipCode: 2101 } }; - assert.deepEqual(exports.roundTripOptionalAllTypesResult(atr_struct), atr_struct); - const atr_array = { tag: AllTypesResultValues.Tag.ArrayPayload, param0: [10, 20, 30] }; - assert.deepEqual(exports.roundTripOptionalAllTypesResult(atr_array), atr_array); + const atr_struct = { + tag: AllTypesResultValues.Tag.StructPayload, + param0: { street: "100 Main St", city: "Boston", zipCode: 2101 }, + }; + assert.deepEqual( + exports.roundTripOptionalAllTypesResult(atr_struct), + atr_struct, + ); + const atr_array = { + tag: AllTypesResultValues.Tag.ArrayPayload, + param0: [10, 20, 30], + }; + assert.deepEqual( + exports.roundTripOptionalAllTypesResult(atr_array), + atr_array, + ); const atr_empty = { tag: AllTypesResultValues.Tag.Empty }; - assert.deepEqual(exports.roundTripOptionalAllTypesResult(atr_empty), atr_empty); + assert.deepEqual( + exports.roundTripOptionalAllTypesResult(atr_empty), + atr_empty, + ); assert.equal(exports.roundTripOptionalAllTypesResult(null), null); // OptionalAllTypesResult — optional struct, class, JSObject, nested enum, array as associated value payloads - const oatr_structSome = { tag: OptionalAllTypesResultValues.Tag.OptStruct, param0: { street: "200 Oak St", city: "Denver", zipCode: null } }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_structSome), oatr_structSome); - - const oatr_structNone = { tag: OptionalAllTypesResultValues.Tag.OptStruct, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_structNone), oatr_structNone); - - const oatr_classSome = { tag: OptionalAllTypesResultValues.Tag.OptClass, param0: new exports.Greeter("OptEnumUser") }; - const oatr_classSome_result = exports.roundTripOptionalPayloadResult(oatr_classSome); - assert.equal(oatr_classSome_result.tag, OptionalAllTypesResultValues.Tag.OptClass); + const oatr_structSome = { + tag: OptionalAllTypesResultValues.Tag.OptStruct, + param0: { street: "200 Oak St", city: "Denver", zipCode: null }, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_structSome), + oatr_structSome, + ); + + const oatr_structNone = { + tag: OptionalAllTypesResultValues.Tag.OptStruct, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_structNone), + oatr_structNone, + ); + + const oatr_classSome = { + tag: OptionalAllTypesResultValues.Tag.OptClass, + param0: new exports.Greeter("OptEnumUser"), + }; + const oatr_classSome_result = + exports.roundTripOptionalPayloadResult(oatr_classSome); + assert.equal( + oatr_classSome_result.tag, + OptionalAllTypesResultValues.Tag.OptClass, + ); assert.equal(oatr_classSome_result.param0.name, "OptEnumUser"); - const oatr_classNone = { tag: OptionalAllTypesResultValues.Tag.OptClass, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_classNone), oatr_classNone); - - const oatr_jsObjectSome = { tag: OptionalAllTypesResultValues.Tag.OptJSObject, param0: { key: "value" } }; - const oatr_jsObjectSome_result = exports.roundTripOptionalPayloadResult(oatr_jsObjectSome); - assert.equal(oatr_jsObjectSome_result.tag, OptionalAllTypesResultValues.Tag.OptJSObject); + const oatr_classNone = { + tag: OptionalAllTypesResultValues.Tag.OptClass, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_classNone), + oatr_classNone, + ); + + const oatr_jsObjectSome = { + tag: OptionalAllTypesResultValues.Tag.OptJSObject, + param0: { key: "value" }, + }; + const oatr_jsObjectSome_result = + exports.roundTripOptionalPayloadResult(oatr_jsObjectSome); + assert.equal( + oatr_jsObjectSome_result.tag, + OptionalAllTypesResultValues.Tag.OptJSObject, + ); assert.equal(oatr_jsObjectSome_result.param0.key, "value"); - const oatr_jsObjectNone = { tag: OptionalAllTypesResultValues.Tag.OptJSObject, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_jsObjectNone), oatr_jsObjectNone); - - const oatr_nestedEnumSome = { tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, param0: { tag: APIResultValues.Tag.Failure, param0: 404 } }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_nestedEnumSome), oatr_nestedEnumSome); - - const oatr_nestedEnumNone = { tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_nestedEnumNone), oatr_nestedEnumNone); - - const oatr_arraySome = { tag: OptionalAllTypesResultValues.Tag.OptArray, param0: [1, 2, 3] }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_arraySome), oatr_arraySome); - - const oatr_arrayNone = { tag: OptionalAllTypesResultValues.Tag.OptArray, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_arrayNone), oatr_arrayNone); - - const oatr_jsClassSome = { tag: OptionalAllTypesResultValues.Tag.OptJsClass, param0: new ImportedFoo("optEnumFoo") }; - const oatr_jsClassSome_result = exports.roundTripOptionalPayloadResult(oatr_jsClassSome); - assert.equal(oatr_jsClassSome_result.tag, OptionalAllTypesResultValues.Tag.OptJsClass); + const oatr_jsObjectNone = { + tag: OptionalAllTypesResultValues.Tag.OptJSObject, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_jsObjectNone), + oatr_jsObjectNone, + ); + + const oatr_nestedEnumSome = { + tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, + param0: { tag: APIResultValues.Tag.Failure, param0: 404 }, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_nestedEnumSome), + oatr_nestedEnumSome, + ); + + const oatr_nestedEnumNone = { + tag: OptionalAllTypesResultValues.Tag.OptNestedEnum, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_nestedEnumNone), + oatr_nestedEnumNone, + ); + + const oatr_arraySome = { + tag: OptionalAllTypesResultValues.Tag.OptArray, + param0: [1, 2, 3], + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_arraySome), + oatr_arraySome, + ); + + const oatr_arrayNone = { + tag: OptionalAllTypesResultValues.Tag.OptArray, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_arrayNone), + oatr_arrayNone, + ); + + const oatr_jsClassSome = { + tag: OptionalAllTypesResultValues.Tag.OptJsClass, + param0: new ImportedFoo("optEnumFoo"), + }; + const oatr_jsClassSome_result = + exports.roundTripOptionalPayloadResult(oatr_jsClassSome); + assert.equal( + oatr_jsClassSome_result.tag, + OptionalAllTypesResultValues.Tag.OptJsClass, + ); assert.equal(oatr_jsClassSome_result.param0.value, "optEnumFoo"); - const oatr_jsClassNone = { tag: OptionalAllTypesResultValues.Tag.OptJsClass, param0: null }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_jsClassNone), oatr_jsClassNone); + const oatr_jsClassNone = { + tag: OptionalAllTypesResultValues.Tag.OptJsClass, + param0: null, + }; + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_jsClassNone), + oatr_jsClassNone, + ); const oatr_empty = { tag: OptionalAllTypesResultValues.Tag.Empty }; - assert.deepEqual(exports.roundTripOptionalPayloadResult(oatr_empty), oatr_empty); + assert.deepEqual( + exports.roundTripOptionalPayloadResult(oatr_empty), + oatr_empty, + ); // Optional OptionalAllTypesResult roundtrip - assert.deepEqual(exports.roundTripOptionalPayloadResultOpt(oatr_structSome), oatr_structSome); - assert.deepEqual(exports.roundTripOptionalPayloadResultOpt(oatr_structNone), oatr_structNone); - assert.deepEqual(exports.roundTripOptionalPayloadResultOpt(oatr_empty), oatr_empty); + assert.deepEqual( + exports.roundTripOptionalPayloadResultOpt(oatr_structSome), + oatr_structSome, + ); + assert.deepEqual( + exports.roundTripOptionalPayloadResultOpt(oatr_structNone), + oatr_structNone, + ); + assert.deepEqual( + exports.roundTripOptionalPayloadResultOpt(oatr_empty), + oatr_empty, + ); assert.equal(exports.roundTripOptionalPayloadResultOpt(null), null); exports.takeOptionalJSObject(null);