Skip to content

Commit 47e7e2a

Browse files
Implemented stack-based JSValue support for array/dictionary payloads by wiring JSValue lower/lift into the stack fragments. Reintroduced JSValue dictionary round-trip coverage: prelude handler and runtime test added, generated bindings refreshed, and all tests (make unittest SWIFT_SDK_ID=DEVELOPMENT-SNAPSHOT-2025-11-03-a-wasm32-unknown-wasip1) now pass.
Files touched: `Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift`, `Tests/prelude.mjs`, `Tests/BridgeJSRuntimeTests/DictionaryTests.swift`, and regenerated BridgeJS outputs.
1 parent ee99ad9 commit 47e7e2a

5 files changed

Lines changed: 90 additions & 2 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2780,7 +2780,23 @@ struct IntrinsicJSFragment: Sendable {
27802780
private static func stackLiftFragment(elementType: BridgeType) throws -> IntrinsicJSFragment {
27812781
switch elementType {
27822782
case .jsValue:
2783-
throw BridgeJSLinkError(message: "Array of JSValue is not supported yet")
2783+
return IntrinsicJSFragment(
2784+
parameters: [],
2785+
printCode: { arguments, scope, printer, cleanup in
2786+
let payload2 = scope.variable("jsValuePayload2")
2787+
let payload1 = scope.variable("jsValuePayload1")
2788+
let kind = scope.variable("jsValueKind")
2789+
printer.write("const \(payload2) = \(JSGlueVariableScope.reservedTmpRetF64s).pop();")
2790+
printer.write("const \(payload1) = \(JSGlueVariableScope.reservedTmpRetInts).pop();")
2791+
printer.write("const \(kind) = \(JSGlueVariableScope.reservedTmpRetInts).pop();")
2792+
let resultVar = scope.variable("jsValue")
2793+
registerJSValueHelpers(scope: scope)
2794+
printer.write(
2795+
"const \(resultVar) = \(jsValueLiftHelperName)(\(kind), \(payload1), \(payload2));"
2796+
)
2797+
return [resultVar]
2798+
}
2799+
)
27842800
case .string:
27852801
return IntrinsicJSFragment(
27862802
parameters: [],
@@ -2944,7 +2960,19 @@ struct IntrinsicJSFragment: Sendable {
29442960
private static func stackLowerFragment(elementType: BridgeType) throws -> IntrinsicJSFragment {
29452961
switch elementType {
29462962
case .jsValue:
2947-
throw BridgeJSLinkError(message: "Array of JSValue is not supported yet")
2963+
return IntrinsicJSFragment(
2964+
parameters: ["value"],
2965+
printCode: { arguments, scope, printer, cleanup in
2966+
let lowered = jsValueLower.printCode(arguments, scope, printer, cleanup)
2967+
let kindVar = lowered[0]
2968+
let payload1Var = lowered[1]
2969+
let payload2Var = lowered[2]
2970+
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(kindVar));")
2971+
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(payload1Var));")
2972+
printer.write("\(JSGlueVariableScope.reservedTmpParamF64s).push(\(payload2Var));")
2973+
return []
2974+
}
2975+
)
29482976
case .string:
29492977
return IntrinsicJSFragment(
29502978
parameters: ["value"],

Tests/BridgeJSRuntimeTests/DictionaryTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ final class DictionaryTests: XCTestCase {
2929
XCTAssertEqual(result, input)
3030
}
3131

32+
func testRoundTripDictionaryJSValue() throws {
33+
let input: [String: JSValue] = [
34+
"number": .number(123.5),
35+
"boolean": .boolean(true),
36+
"string": .string("hello"),
37+
"null": .null,
38+
]
39+
let result = try jsRoundTripDictionaryJSValue(input)
40+
XCTAssertEqual(result, input)
41+
}
42+
3243
func testRoundTripNestedDictionary() throws {
3344
let input: [String: [Double]] = [
3445
"xs": [1.0, 2.5],
@@ -74,6 +85,8 @@ final class DictionaryTests: XCTestCase {
7485

7586
@JSFunction func jsRoundTripDictionaryJSObject(_ values: [String: JSObject]) throws(JSException) -> [String: JSObject]
7687

88+
@JSFunction func jsRoundTripDictionaryJSValue(_ values: [String: JSValue]) throws(JSException) -> [String: JSValue]
89+
7790
@JSFunction func jsRoundTripNestedDictionary(_ values: [String: [Double]]) throws(JSException) -> [String: [Double]]
7891

7992
@JSFunction func jsRoundTripOptionalDictionary(_ values: [String: String]?) throws(JSException) -> [String: String]?

Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8478,6 +8478,24 @@ func _$jsRoundTripDictionaryJSObject(_ values: [String: JSObject]) throws(JSExce
84788478
return [String: JSObject].bridgeJSLiftReturn()
84798479
}
84808480

8481+
#if arch(wasm32)
8482+
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripDictionaryJSValue")
8483+
fileprivate func bjs_jsRoundTripDictionaryJSValue() -> Void
8484+
#else
8485+
fileprivate func bjs_jsRoundTripDictionaryJSValue() -> Void {
8486+
fatalError("Only available on WebAssembly")
8487+
}
8488+
#endif
8489+
8490+
func _$jsRoundTripDictionaryJSValue(_ values: [String: JSValue]) throws(JSException) -> [String: JSValue] {
8491+
let _ = values.bridgeJSLowerParameter()
8492+
bjs_jsRoundTripDictionaryJSValue()
8493+
if let error = _swift_js_take_exception() {
8494+
throw error
8495+
}
8496+
return [String: JSValue].bridgeJSLiftReturn()
8497+
}
8498+
84818499
#if arch(wasm32)
84828500
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripNestedDictionary")
84838501
fileprivate func bjs_jsRoundTripNestedDictionary() -> Void

Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12868,6 +12868,32 @@
1286812868
}
1286912869
}
1287012870
},
12871+
{
12872+
"name" : "jsRoundTripDictionaryJSValue",
12873+
"parameters" : [
12874+
{
12875+
"name" : "values",
12876+
"type" : {
12877+
"dictionary" : {
12878+
"_0" : {
12879+
"jsValue" : {
12880+
12881+
}
12882+
}
12883+
}
12884+
}
12885+
}
12886+
],
12887+
"returnType" : {
12888+
"dictionary" : {
12889+
"_0" : {
12890+
"jsValue" : {
12891+
12892+
}
12893+
}
12894+
}
12895+
}
12896+
},
1287112897
{
1287212898
"name" : "jsRoundTripNestedDictionary",
1287312899
"parameters" : [

Tests/prelude.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ export async function setupOptions(options, context) {
7474
"jsRoundTripDictionaryJSObject": (dict) => {
7575
return dict;
7676
},
77+
"jsRoundTripDictionaryJSValue": (dict) => {
78+
return dict;
79+
},
7780
"jsRoundTripNestedDictionary": (dict) => {
7881
return Object.fromEntries(Object.entries(dict).map(([k, v]) => [k, [...v]]));
7982
},

0 commit comments

Comments
 (0)