From b8003b8e945656e951b2174f317de1adc66f7555 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Tue, 3 Nov 2026 02:23:30 +0100 Subject: [PATCH 1/2] BridgeJS: Diagnose async and throwing @JS struct initializers --- .../Sources/BridgeJSCore/SwiftToSkeleton.swift | 8 ++++++++ .../BridgeJSToolTests/DiagnosticsTests.swift | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index ef39000b1..7e075a8af 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -1986,6 +1986,14 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor { guard let effects = collectEffects(signature: node.signature) else { return .skipChildren } + if effects.isAsync || effects.isThrows { + diagnose( + node: node, + message: "@JS struct initializer cannot be async or throw", + hint: "BridgeJS calls this initializer to rebuild struct values passed from JavaScript" + ) + return .skipChildren + } let structAbiName = exportedStructByName[structKey]?.abiName ?? "unknown" let constructor = ExportedConstructor( diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift index 580360bb7..aadf04f00 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift @@ -535,6 +535,21 @@ import Testing #expect(skeleton.exported != nil) } + @Test + func throwingStructInitProducesDiagnostic() throws { + let source = """ + @JS struct Point { + var x: Double + + @JS init(x: Double) throws(JSException) { + self.x = x + } + } + """ + let diagnostics = try #require(moduleDiagnostics(source: source)) + #expect(diagnostics.description.contains("@JS struct initializer cannot be async or throw")) + } + @Test func structWithoutExplicitInitSucceeds() throws { let source = """ From f1d5c94c4239d0fd9e398eb3ed19db98a2c629ae Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Tue, 3 Nov 2026 02:21:18 +0100 Subject: [PATCH 2/2] BridgeJS: Fix JSValue reference ownership across the bridge --- .../Sources/BridgeJSLink/JSGlueGen.swift | 124 +++++++++--------- .../__Snapshots__/BridgeJSLinkTests/Alias.js | 9 +- .../BridgeJSLinkTests/ArrayTypes.js | 9 +- .../__Snapshots__/BridgeJSLinkTests/Async.js | 9 +- .../AsyncAssociatedValueEnum.js | 7 + .../BridgeJSLinkTests/AsyncImport.js | 7 + .../BridgeJSLinkTests/AsyncStaticImport.js | 7 + .../BridgeJSLinkTests/DefaultParameters.js | 9 +- .../BridgeJSLinkTests/DictionaryTypes.js | 9 +- .../BridgeJSLinkTests/EnumAssociatedValue.js | 9 +- .../BridgeJSLinkTests/EnumRawType.js | 9 +- .../BridgeJSLinkTests/GenericImports.js | 9 +- .../BridgeJSLinkTests/ImportArray.js | 9 +- .../ImportedTypeInExportedInterface.js | 9 +- .../BridgeJSLinkTests/JSValue.js | 21 ++- .../BridgeJSLinkTests/Namespaces.Global.js | 9 +- .../BridgeJSLinkTests/Namespaces.js | 9 +- .../BridgeJSLinkTests/Optionals.js | 9 +- .../BridgeJSLinkTests/Protocol.js | 9 +- .../BridgeJSLinkTests/SwiftClosure.js | 9 +- .../BridgeJSLinkTests/SwiftClosureImports.js | 7 + .../BridgeJSLinkTests/SwiftStruct.js | 9 +- .../BridgeJSLinkTests/SwiftStructImports.js | 9 +- .../JavaScriptKit/BridgeJSIntrinsics.swift | 18 +-- .../BridgeJSRuntimeTests/ExportAPITests.swift | 8 ++ .../Generated/BridgeJS.swift | 22 ++++ .../Generated/JavaScript/BridgeJS.json | 34 +++++ Tests/prelude.mjs | 17 ++- 28 files changed, 330 insertions(+), 95 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 673a64212..20f231dc8 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -749,6 +749,27 @@ struct IntrinsicJSFragment: Sendable { private static let jsValueLowerHelperName = "__bjs_jsValueLower" private static let jsValueLiftHelperName = "__bjs_jsValueLift" + private static let jsValueTakeHelperName = "__bjs_jsValueTake" + + /// Mirrors `JavaScriptValueKind` in `_CJavaScriptKit.h`. + private enum JSValueKind: Int, CaseIterable { + case boolean = 0 + case string = 1 + case number = 2 + case object = 3 + case null = 4 + case undefined = 5 + case symbol = 7 + case bigInt = 8 + + /// Whether `payload1` is a reference in `swift.memory`. + var holdsReference: Bool { + switch self { + case .string, .object, .symbol, .bigInt: return true + case .boolean, .number, .null, .undefined: return false + } + } + } private static func registerJSValueHelpers(scope: JSGlueVariableScope) { scope.registerIntrinsic("jsValueHelpers") { helperPrinter in @@ -777,6 +798,20 @@ struct IntrinsicJSFragment: Sendable { helperPrinter.write("return \(resultVar);") } helperPrinter.write("}") + helperPrinter.write("function \(jsValueTakeHelperName)(kind, payload1, payload2) {") + helperPrinter.indent { + helperPrinter.write("const value = \(jsValueLiftHelperName)(kind, payload1, payload2);") + let holdsReference = JSValueKind.allCases.filter(\.holdsReference) + .map { "kind === \($0.rawValue)" } + .joined(separator: " || ") + helperPrinter.write("if (\(holdsReference)) {") + helperPrinter.indent { + helperPrinter.write("\(JSGlueVariableScope.reservedSwift).memory.release(payload1);") + } + helperPrinter.write("}") + helperPrinter.write("return value;") + } + helperPrinter.write("}") } } @@ -792,7 +827,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("let \(payload2Var);") printer.write("if (\(value) === null) {") printer.indent { - printer.write("\(kindVar) = 4;") + printer.write("\(kindVar) = \(JSValueKind.null.rawValue);") printer.write("\(payload1Var) = 0;") printer.write("\(payload2Var) = 0;") } @@ -802,22 +837,22 @@ struct IntrinsicJSFragment: Sendable { printer.indent { printer.write("case \"boolean\":") printer.indent { - printer.write("\(kindVar) = 0;") + printer.write("\(kindVar) = \(JSValueKind.boolean.rawValue);") printer.write("\(payload1Var) = \(value) ? 1 : 0;") printer.write("\(payload2Var) = 0;") printer.write("break;") } printer.write("case \"number\":") printer.indent { - printer.write("\(kindVar) = 2;") + printer.write("\(kindVar) = \(JSValueKind.number.rawValue);") printer.write("\(payload1Var) = 0;") printer.write("\(payload2Var) = \(value);") printer.write("break;") } - func emitRetainCase(_ caseName: String, kind: Int) { + func emitRetainCase(_ caseName: String, kind: JSValueKind) { printer.write("case \"\(caseName)\":") printer.indent { - printer.write("\(kindVar) = \(kind);") + printer.write("\(kindVar) = \(kind.rawValue);") printer.write( "\(payload1Var) = \(JSGlueVariableScope.reservedSwift).memory.retain(\(value));" ) @@ -825,18 +860,18 @@ struct IntrinsicJSFragment: Sendable { printer.write("break;") } } - emitRetainCase("string", kind: 1) + emitRetainCase("string", kind: .string) printer.write("case \"undefined\":") printer.indent { - printer.write("\(kindVar) = 5;") + printer.write("\(kindVar) = \(JSValueKind.undefined.rawValue);") printer.write("\(payload1Var) = 0;") printer.write("\(payload2Var) = 0;") printer.write("break;") } - emitRetainCase("object", kind: 3) - emitRetainCase("function", kind: 3) - emitRetainCase("symbol", kind: 7) - emitRetainCase("bigint", kind: 8) + emitRetainCase("object", kind: .object) + emitRetainCase("function", kind: .object) + emitRetainCase("symbol", kind: .symbol) + emitRetainCase("bigint", kind: .bigInt) printer.write("default:") printer.indent { printer.write("throw new TypeError(\"Unsupported JSValue type\");") @@ -874,53 +909,21 @@ struct IntrinsicJSFragment: Sendable { printer.write("let \(resultVar);") printer.write("switch (\(kind)) {") printer.indent { - printer.write("case 0:") - printer.indent { - printer.write("\(resultVar) = \(payload1) !== 0;") - printer.write("break;") - } - printer.write("case 1:") - printer.indent { - printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(payload1));" - ) - printer.write("break;") - } - printer.write("case 2:") - printer.indent { - printer.write("\(resultVar) = \(payload2);") - printer.write("break;") - } - printer.write("case 3:") - printer.indent { - printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(payload1));" - ) - printer.write("break;") - } - printer.write("case 4:") - printer.indent { - printer.write("\(resultVar) = null;") - printer.write("break;") - } - printer.write("case 5:") - printer.indent { - printer.write("\(resultVar) = undefined;") - printer.write("break;") - } - printer.write("case 7:") - printer.indent { - printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(payload1));" - ) - printer.write("break;") - } - printer.write("case 8:") - printer.indent { - printer.write( - "\(resultVar) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(payload1));" - ) - printer.write("break;") + for valueKind in JSValueKind.allCases { + let value: String + switch valueKind { + case .boolean: value = "\(payload1) !== 0" + case .number: value = payload2 + case .null: value = "null" + case .undefined: value = "undefined" + case .string, .object, .symbol, .bigInt: + value = "\(JSGlueVariableScope.reservedSwift).memory.getObject(\(payload1))" + } + printer.write("case \(valueKind.rawValue):") + printer.indent { + printer.write("\(resultVar) = \(value);") + printer.write("break;") + } } printer.write("default:") printer.indent { @@ -961,11 +964,12 @@ struct IntrinsicJSFragment: Sendable { let resultVar = scope.variable("jsValue") registerJSValueHelpers(scope: scope) printer.write( - "const \(resultVar) = \(jsValueLiftHelperName)(\(kind), \(payload1), \(payload2));" + "const \(resultVar) = \(jsValueTakeHelperName)(\(kind), \(payload1), \(payload2));" ) return [resultVar] } ) + /// Swift lends the payload reference for the duration of the call, so it is not released here. static let jsValueLiftParameter = IntrinsicJSFragment( parameters: ["kind", "payload1", "payload2"], printCode: { arguments, context in @@ -2232,7 +2236,7 @@ struct IntrinsicJSFragment: Sendable { let resultVar = scope.variable("jsValue") registerJSValueHelpers(scope: scope) printer.write( - "const \(resultVar) = \(jsValueLiftHelperName)(\(kindVar), \(payload1Var), \(payload2Var));" + "const \(resultVar) = \(jsValueTakeHelperName)(\(kindVar), \(payload1Var), \(payload2Var));" ) return [resultVar] } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Alias.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Alias.js index b434d4e5e..adc2e0fc9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Alias.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Alias.js @@ -267,7 +267,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -361,6 +361,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT16PolygonReference = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js index f4c519fe9..6365fd99e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js @@ -274,7 +274,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -368,6 +368,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Array_Int = __bjs_arrayCodec(__bjs_primitiveCodecs.Int); const __bjs_codec_Array_String = __bjs_arrayCodec(__bjs_stringCodec); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js index 6cc51f282..980dd9fdc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Async.js @@ -271,7 +271,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -365,6 +365,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT10AsyncPoint = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncAssociatedValueEnum.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncAssociatedValueEnum.js index 099a0756e..69f17eae3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncAssociatedValueEnum.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncAssociatedValueEnum.js @@ -128,6 +128,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_createEnumHelpers_M10TestModuleT18AsyncPayloadResult = () => ({ lower: (value) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js index 856c42435..db15b92a8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncImport.js @@ -121,6 +121,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { if (state.unregistered) { return; } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js index 36d839ed9..f6da4e275 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/AsyncStaticImport.js @@ -121,6 +121,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { if (state.unregistered) { return; } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js index 03afa8031..cbca91ea2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js @@ -267,7 +267,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -361,6 +361,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT6Config = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js index 10f84209a..ab6cf6ebe 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Dict_Int = __bjs_dictCodec(__bjs_primitiveCodecs.Int); const __bjs_codec_Dict_String = __bjs_dictCodec(__bjs_stringCodec); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js index 3f0c005f2..36d219f64 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.js @@ -342,7 +342,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -436,6 +436,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Optional_String = __bjs_optionalCodec(__bjs_stringCodec); const __bjs_codec_Optional_Bool = __bjs_optionalCodec(__bjs_primitiveCodecs.Bool); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js index ce8d4ed42..e33778458 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.js @@ -336,7 +336,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -430,6 +430,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT8FileSize = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GenericImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GenericImports.js index 49aaa5798..d6250c200 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GenericImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/GenericImports.js @@ -295,7 +295,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -389,6 +389,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Array_Int = __bjs_arrayCodec(__bjs_primitiveCodecs.Int); const __bjs_codec_Optional_Array_Int = __bjs_optionalCodec(__bjs_codec_Array_Int); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js index a3e0fd6d3..2a64c6775 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportArray.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Array_Int = __bjs_arrayCodec(__bjs_primitiveCodecs.Int); const __bjs_codec_Array_String = __bjs_arrayCodec(__bjs_stringCodec); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js index 2f45f8eed..3c4743ee6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ImportedTypeInExportedInterface.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Foo = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js index 07d7ba167..afccabed4 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Array_JSValue = __bjs_arrayCodec(__bjs_primitiveCodecs.JSValue); const __bjs_codec_Optional_Array_JSValue = __bjs_optionalCodec(__bjs_codec_Array_JSValue); @@ -821,7 +828,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; } echoOptional(value) { @@ -844,7 +851,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); optResult = jsValue; } else { optResult = null; @@ -856,7 +863,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; } set value(value) { @@ -871,7 +878,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); optResult = jsValue; } else { optResult = null; @@ -901,7 +908,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, roundTripOptionalJSValue: function bjs_roundTripOptionalJSValue(value) { @@ -924,7 +931,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); optResult = jsValue; } else { optResult = null; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js index a5f6d34f4..e76f92d8f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Global.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT7Greeter = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js index b13010601..31e413e1f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT7Greeter = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js index fb8018c44..7aaa5296c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_JSObject = { lower: (v) => { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js index 8802e8786..945759fb1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.js @@ -285,7 +285,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -379,6 +379,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { if (state.unregistered) { return; } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js index 3f4ab6e05..082a42323 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosure.js @@ -291,7 +291,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -385,6 +385,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { if (state.unregistered) { return; } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js index b15678998..3045dd8cb 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClosureImports.js @@ -121,6 +121,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { if (state.unregistered) { return; } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js index 54aa63d69..39ba60207 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStruct.js @@ -266,7 +266,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -360,6 +360,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_Optional_Int = __bjs_optionalCodec(__bjs_primitiveCodecs.Int); const __bjs_codec_Optional_Bool = __bjs_optionalCodec(__bjs_primitiveCodecs.Bool); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js index 2ebef1501..0f0838b41 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftStructImports.js @@ -261,7 +261,7 @@ export async function createInstantiator(options, swift) { const jsValuePayload2 = f64Stack.pop(); const jsValuePayload1 = i32Stack.pop(); const jsValueKind = i32Stack.pop(); - const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2); + const jsValue = __bjs_jsValueTake(jsValueKind, jsValuePayload1, jsValuePayload2); return jsValue; }, }, @@ -355,6 +355,13 @@ export async function createInstantiator(options, swift) { } return jsValue; } + function __bjs_jsValueTake(kind, payload1, payload2) { + const value = __bjs_jsValueLift(kind, payload1, payload2); + if (kind === 1 || kind === 3 || kind === 7 || kind === 8) { + swift.memory.release(payload1); + } + return value; + } const __bjs_codec_M10TestModuleT5Point = { lower: (v) => { diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index dd5e1e970..0b7fc3120 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -969,11 +969,14 @@ extension JSValue: _BridgedSwiftStackType { } @_spi(BridgeJS) public consuming func bridgeJSStackPush() -> Void { - let lowered = bridgeJSLowerParameter() - let retainedPayload1 = Self.bridgeJSRetainPayloadIfNeeded(kind: lowered.kind, payload1: lowered.payload1) - _swift_js_push_i32(lowered.kind) - _swift_js_push_i32(retainedPayload1) - _swift_js_push_f64(lowered.payload2) + // Retain inside `withRawJSValue` so `self` still owns the JS reference. + withRawJSValue { raw in + let kind = Int32(raw.kind.rawValue) + let payload1 = Int32(bitPattern: raw.payload1) + _swift_js_push_i32(kind) + _swift_js_push_i32(Self.bridgeJSRetainPayloadIfNeeded(kind: kind, payload1: payload1)) + _swift_js_push_f64(raw.payload2) + } } @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (kind: Int32, payload1: Int32, payload2: Double) { @@ -986,19 +989,18 @@ extension JSValue: _BridgedSwiftStackType { } } + /// Takes ownership of the payload reference that JavaScript retained when it lowered the value. @_spi(BridgeJS) public static func bridgeJSLiftParameter( _ kind: Int32, _ payload1: Int32, _ payload2: Double ) -> JSValue { - let retainedPayload1 = bridgeJSRetainPayloadIfNeeded(kind: kind, payload1: payload1) - guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { fatalError("Invalid JSValue kind: \(kind)") } let rawValue = RawJSValue( kind: kindEnum, - payload1: JavaScriptPayload1(UInt32(bitPattern: retainedPayload1)), + payload1: JavaScriptPayload1(UInt32(bitPattern: payload1)), payload2: payload2 ) return rawValue.jsValue diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 55ab78e7a..a3e60711f 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -73,6 +73,14 @@ func runJsWorks() -> Void return v } +@JS func makeJSValueString() -> JSValue { + return .string("makeJSValueString") +} + +@JS func makeJSValueObject() -> JSValue { + return .object(JSObject()) +} + @JS func roundTripOptionalJSValue(v: JSValue?) -> JSValue? { return v } diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 3b82a15d5..8bfe460fc 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -10531,6 +10531,28 @@ public func _bjs_roundTripJSValue(_ vKind: Int32, _ vPayload1: Int32, _ vPayload #endif } +@_expose(wasm, "bjs_makeJSValueString") +@_cdecl("bjs_makeJSValueString") +public func _bjs_makeJSValueString() -> Void { + #if arch(wasm32) + let ret = makeJSValueString() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_makeJSValueObject") +@_cdecl("bjs_makeJSValueObject") +public func _bjs_makeJSValueObject() -> Void { + #if arch(wasm32) + let ret = makeJSValueObject() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripOptionalJSValue") @_cdecl("bjs_roundTripOptionalJSValue") public func _bjs_roundTripOptionalJSValue(_ vIsSome: Int32, _ vKind: Int32, _ vPayload1: Int32, _ vPayload2: Float64) -> Void { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index c4fd1d602..fb4d924e7 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -15686,6 +15686,40 @@ } } }, + { + "abiName" : "bjs_makeJSValueString", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "makeJSValueString", + "parameters" : [ + + ], + "returnType" : { + "jsValue" : { + + } + } + }, + { + "abiName" : "bjs_makeJSValueObject", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "makeJSValueObject", + "parameters" : [ + + ], + "returnType" : { + "jsValue" : { + + } + } + }, { "abiName" : "bjs_roundTripOptionalJSValue", "effects" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index e1d856647..6c53eb59f 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -227,7 +227,7 @@ export async function setupOptions(options, context) { }; }, addToCoreImports(importObject, importsContext) { - const { getInstance, getExports } = importsContext; + const { getInstance, getExports, _swift } = importsContext; options.addToCoreImports?.(importObject, importsContext); importObject["JavaScriptEventLoopTestSupportTests"] = { "isMainThread": () => context.isMainThread, @@ -238,7 +238,7 @@ export async function setupOptions(options, context) { if (!exports) { throw new Error("No exports!?"); } - return BridgeJSRuntimeTests_runJsWorks(getInstance(), exports); + return BridgeJSRuntimeTests_runJsWorks(getInstance(), exports, _swift); } bridgeJSRuntimeTests["runJsStructWorks"] = () => { const exports = getExports(); @@ -267,7 +267,7 @@ export async function setupOptions(options, context) { import assert from "node:assert"; /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ -function BridgeJSRuntimeTests_runJsWorks(instance, exports) { +function BridgeJSRuntimeTests_runJsWorks(instance, exports, swift) { exports.roundTripVoid(); for (const v of [ 0.0, 1.0, -1.0, @@ -610,6 +610,17 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { const result = exports.roundTripJSValue(value); assert.strictEqual(result, value); } + assert.strictEqual(exports.makeJSValueString(), "makeJSValueString"); + const madeObject = exports.makeJSValueObject(); + assert.deepStrictEqual(madeObject, {}); + + const isRetained = (value) => swift.memory._slotByValue.has(value); + assert.equal(isRetained(madeObject), false); + for (const value of [{}, "roundTripJSValue-ownership", Symbol("ownership"), 0xfedcba9876543210n]) { + assert.strictEqual(exports.roundTripJSValue(value), value); + assert.deepStrictEqual(exports.roundTripOptionalJSValueArray([value]), [value]); + assert.equal(isRetained(value), false); + } assert.strictEqual(exports.roundTripOptionalJSValue(null), null); assert.strictEqual(exports.roundTripOptionalJSValue(undefined), null);