Skip to content

Commit d48acfa

Browse files
authored
Merge pull request #580 from swiftwasm/katei/6700-support-jsvalue
BridgeJS: Add support for `[JSValue]`
2 parents f03500f + 3dba5df commit d48acfa

File tree

14 files changed

+590
-2
lines changed

14 files changed

+590
-2
lines changed

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,20 @@ struct IntrinsicJSFragment: Sendable {
10231023
printer.write("\(JSGlueVariableScope.reservedTmpRetF64s).push(\(payload2Var));")
10241024
}
10251025
printer.write("\(JSGlueVariableScope.reservedTmpRetInts).push(\(isSomeVar) ? 1 : 0);")
1026+
case .array(let elementType):
1027+
printer.write("if (\(isSomeVar)) {")
1028+
printer.indent {
1029+
let arrayLowerFragment = try! arrayLower(elementType: elementType)
1030+
let arrayCleanup = CodeFragmentPrinter()
1031+
let _ = arrayLowerFragment.printCode([value], scope, printer, arrayCleanup)
1032+
if !arrayCleanup.lines.isEmpty {
1033+
for line in arrayCleanup.lines {
1034+
printer.write(line)
1035+
}
1036+
}
1037+
}
1038+
printer.write("}")
1039+
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);")
10261040
case .rawValueEnum(_, let rawType):
10271041
switch rawType {
10281042
case .string:
@@ -2601,7 +2615,23 @@ struct IntrinsicJSFragment: Sendable {
26012615
private static func stackLiftFragment(elementType: BridgeType) throws -> IntrinsicJSFragment {
26022616
switch elementType {
26032617
case .jsValue:
2604-
throw BridgeJSLinkError(message: "Array of JSValue is not supported yet")
2618+
return IntrinsicJSFragment(
2619+
parameters: [],
2620+
printCode: { _, scope, printer, cleanup in
2621+
registerJSValueHelpers(scope: scope)
2622+
let payload2Var = scope.variable("jsValuePayload2")
2623+
let payload1Var = scope.variable("jsValuePayload1")
2624+
let kindVar = scope.variable("jsValueKind")
2625+
printer.write("const \(payload2Var) = \(JSGlueVariableScope.reservedTmpRetF64s).pop();")
2626+
printer.write("const \(payload1Var) = \(JSGlueVariableScope.reservedTmpRetInts).pop();")
2627+
printer.write("const \(kindVar) = \(JSGlueVariableScope.reservedTmpRetInts).pop();")
2628+
let resultVar = scope.variable("jsValue")
2629+
printer.write(
2630+
"const \(resultVar) = \(jsValueLiftHelperName)(\(kindVar), \(payload1Var), \(payload2Var));"
2631+
)
2632+
return [resultVar]
2633+
}
2634+
)
26052635
case .string:
26062636
return IntrinsicJSFragment(
26072637
parameters: [],
@@ -2763,7 +2793,20 @@ struct IntrinsicJSFragment: Sendable {
27632793
private static func stackLowerFragment(elementType: BridgeType) throws -> IntrinsicJSFragment {
27642794
switch elementType {
27652795
case .jsValue:
2766-
throw BridgeJSLinkError(message: "Array of JSValue is not supported yet")
2796+
return IntrinsicJSFragment(
2797+
parameters: ["value"],
2798+
printCode: { arguments, scope, printer, cleanup in
2799+
registerJSValueHelpers(scope: scope)
2800+
let lowered = jsValueLower.printCode([arguments[0]], scope, printer, cleanup)
2801+
let kindVar = lowered[0]
2802+
let payload1Var = lowered[1]
2803+
let payload2Var = lowered[2]
2804+
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(kindVar));")
2805+
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(payload1Var));")
2806+
printer.write("\(JSGlueVariableScope.reservedTmpParamF64s).push(\(payload2Var));")
2807+
return []
2808+
}
2809+
)
27672810
case .string:
27682811
return IntrinsicJSFragment(
27692812
parameters: ["value"],

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/JSValue.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
return value
77
}
88

9+
@JS func roundTripJSValueArray(_ values: [JSValue]) -> [JSValue] {
10+
return values
11+
}
12+
13+
@JS func roundTripOptionalJSValueArray(_ values: [JSValue]?) -> [JSValue]? {
14+
return values
15+
}
16+
917
@JS class JSValueHolder {
1018
@JS var value: JSValue
1119
@JS var optionalValue: JSValue?
@@ -30,3 +38,4 @@
3038
}
3139

3240
@JSFunction func jsEchoJSValue(_ value: JSValue) throws(JSException) -> JSValue
41+
@JSFunction func jsEchoJSValueArray(_ values: [JSValue]) throws(JSException) -> [JSValue]

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,82 @@
231231
"_1" : "null"
232232
}
233233
}
234+
},
235+
{
236+
"abiName" : "bjs_roundTripJSValueArray",
237+
"effects" : {
238+
"isAsync" : false,
239+
"isStatic" : false,
240+
"isThrows" : false
241+
},
242+
"name" : "roundTripJSValueArray",
243+
"parameters" : [
244+
{
245+
"label" : "_",
246+
"name" : "values",
247+
"type" : {
248+
"array" : {
249+
"_0" : {
250+
"jsValue" : {
251+
252+
}
253+
}
254+
}
255+
}
256+
}
257+
],
258+
"returnType" : {
259+
"array" : {
260+
"_0" : {
261+
"jsValue" : {
262+
263+
}
264+
}
265+
}
266+
}
267+
},
268+
{
269+
"abiName" : "bjs_roundTripOptionalJSValueArray",
270+
"effects" : {
271+
"isAsync" : false,
272+
"isStatic" : false,
273+
"isThrows" : false
274+
},
275+
"name" : "roundTripOptionalJSValueArray",
276+
"parameters" : [
277+
{
278+
"label" : "_",
279+
"name" : "values",
280+
"type" : {
281+
"nullable" : {
282+
"_0" : {
283+
"array" : {
284+
"_0" : {
285+
"jsValue" : {
286+
287+
}
288+
}
289+
}
290+
},
291+
"_1" : "null"
292+
}
293+
}
294+
}
295+
],
296+
"returnType" : {
297+
"nullable" : {
298+
"_0" : {
299+
"array" : {
300+
"_0" : {
301+
"jsValue" : {
302+
303+
}
304+
}
305+
}
306+
},
307+
"_1" : "null"
308+
}
309+
}
234310
}
235311
],
236312
"protocols" : [
@@ -261,6 +337,32 @@
261337

262338
}
263339
}
340+
},
341+
{
342+
"name" : "jsEchoJSValueArray",
343+
"parameters" : [
344+
{
345+
"name" : "values",
346+
"type" : {
347+
"array" : {
348+
"_0" : {
349+
"jsValue" : {
350+
351+
}
352+
}
353+
}
354+
}
355+
}
356+
],
357+
"returnType" : {
358+
"array" : {
359+
"_0" : {
360+
"jsValue" : {
361+
362+
}
363+
}
364+
}
365+
}
264366
}
265367
],
266368
"types" : [

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,37 @@ public func _bjs_roundTripOptionalJSValue(_ valueIsSome: Int32, _ valueKind: Int
2020
#endif
2121
}
2222

23+
@_expose(wasm, "bjs_roundTripJSValueArray")
24+
@_cdecl("bjs_roundTripJSValueArray")
25+
public func _bjs_roundTripJSValueArray() -> Void {
26+
#if arch(wasm32)
27+
let ret = roundTripJSValueArray(_: [JSValue].bridgeJSLiftParameter())
28+
ret.bridgeJSLowerReturn()
29+
#else
30+
fatalError("Only available on WebAssembly")
31+
#endif
32+
}
33+
34+
@_expose(wasm, "bjs_roundTripOptionalJSValueArray")
35+
@_cdecl("bjs_roundTripOptionalJSValueArray")
36+
public func _bjs_roundTripOptionalJSValueArray(_ values: Int32) -> Void {
37+
#if arch(wasm32)
38+
let ret = roundTripOptionalJSValueArray(_: {
39+
if values == 0 {
40+
return Optional<[JSValue]>.none
41+
} else {
42+
return [JSValue].bridgeJSLiftParameter()
43+
}
44+
}())
45+
let __bjs_isSome_ret = ret != nil
46+
if let __bjs_unwrapped_ret = ret {
47+
__bjs_unwrapped_ret.bridgeJSLowerReturn()}
48+
_swift_js_push_i32(__bjs_isSome_ret ? 1 : 0)
49+
#else
50+
fatalError("Only available on WebAssembly")
51+
#endif
52+
}
53+
2354
@_expose(wasm, "bjs_JSValueHolder_init")
2455
@_cdecl("bjs_JSValueHolder_init")
2556
public func _bjs_JSValueHolder_init(_ valueKind: Int32, _ valuePayload1: Int32, _ valuePayload2: Float64, _ optionalValueIsSome: Int32, _ optionalValueKind: Int32, _ optionalValuePayload1: Int32, _ optionalValuePayload2: Float64) -> UnsafeMutableRawPointer {
@@ -146,4 +177,22 @@ func _$jsEchoJSValue(_ value: JSValue) throws(JSException) -> JSValue {
146177
throw error
147178
}
148179
return JSValue.bridgeJSLiftReturn()
180+
}
181+
182+
#if arch(wasm32)
183+
@_extern(wasm, module: "TestModule", name: "bjs_jsEchoJSValueArray")
184+
fileprivate func bjs_jsEchoJSValueArray() -> Void
185+
#else
186+
fileprivate func bjs_jsEchoJSValueArray() -> Void {
187+
fatalError("Only available on WebAssembly")
188+
}
189+
#endif
190+
191+
func _$jsEchoJSValueArray(_ values: [JSValue]) throws(JSException) -> [JSValue] {
192+
let _ = values.bridgeJSLowerParameter()
193+
bjs_jsEchoJSValueArray()
194+
if let error = _swift_js_take_exception() {
195+
throw error
196+
}
197+
return [JSValue].bridgeJSLiftReturn()
149198
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ export type Exports = {
2424
}
2525
roundTripJSValue(value: any): any;
2626
roundTripOptionalJSValue(value: any | null): any | null;
27+
roundTripJSValueArray(values: any[]): any[];
28+
roundTripOptionalJSValueArray(values: any[] | null): any[] | null;
2729
}
2830
export type Imports = {
2931
jsEchoJSValue(value: any): any;
32+
jsEchoJSValueArray(values: any[]): any[];
3033
}
3134
export function createInstantiator(options: {
3235
imports: Imports;

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/JSValue.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,31 @@ export async function createInstantiator(options, swift) {
312312
setException(error);
313313
}
314314
}
315+
TestModule["bjs_jsEchoJSValueArray"] = function bjs_jsEchoJSValueArray() {
316+
try {
317+
const arrayLen = tmpRetInts.pop();
318+
const arrayResult = [];
319+
for (let i = 0; i < arrayLen; i++) {
320+
const jsValuePayload2 = tmpRetF64s.pop();
321+
const jsValuePayload1 = tmpRetInts.pop();
322+
const jsValueKind = tmpRetInts.pop();
323+
const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2);
324+
arrayResult.push(jsValue);
325+
}
326+
arrayResult.reverse();
327+
let ret = imports.jsEchoJSValueArray(arrayResult);
328+
const arrayCleanups = [];
329+
for (const elem of ret) {
330+
const [elemKind, elemPayload1, elemPayload2] = __bjs_jsValueLower(elem);
331+
tmpParamInts.push(elemKind);
332+
tmpParamInts.push(elemPayload1);
333+
tmpParamF64s.push(elemPayload2);
334+
}
335+
tmpParamInts.push(ret.length);
336+
} catch (error) {
337+
setException(error);
338+
}
339+
}
315340
},
316341
setInstance: (i) => {
317342
instance = i;
@@ -448,6 +473,64 @@ export async function createInstantiator(options, swift) {
448473
}
449474
return optResult;
450475
},
476+
roundTripJSValueArray: function bjs_roundTripJSValueArray(values) {
477+
const arrayCleanups = [];
478+
for (const elem of values) {
479+
const [elemKind, elemPayload1, elemPayload2] = __bjs_jsValueLower(elem);
480+
tmpParamInts.push(elemKind);
481+
tmpParamInts.push(elemPayload1);
482+
tmpParamF64s.push(elemPayload2);
483+
}
484+
tmpParamInts.push(values.length);
485+
instance.exports.bjs_roundTripJSValueArray();
486+
const arrayLen = tmpRetInts.pop();
487+
const arrayResult = [];
488+
for (let i = 0; i < arrayLen; i++) {
489+
const jsValuePayload2 = tmpRetF64s.pop();
490+
const jsValuePayload1 = tmpRetInts.pop();
491+
const jsValueKind = tmpRetInts.pop();
492+
const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2);
493+
arrayResult.push(jsValue);
494+
}
495+
arrayResult.reverse();
496+
for (const cleanup of arrayCleanups) { cleanup(); }
497+
return arrayResult;
498+
},
499+
roundTripOptionalJSValueArray: function bjs_roundTripOptionalJSValueArray(values) {
500+
const isSome = values != null;
501+
const valuesCleanups = [];
502+
if (isSome) {
503+
const arrayCleanups = [];
504+
for (const elem of values) {
505+
const [elemKind, elemPayload1, elemPayload2] = __bjs_jsValueLower(elem);
506+
tmpParamInts.push(elemKind);
507+
tmpParamInts.push(elemPayload1);
508+
tmpParamF64s.push(elemPayload2);
509+
}
510+
tmpParamInts.push(values.length);
511+
valuesCleanups.push(() => { for (const cleanup of arrayCleanups) { cleanup(); } });
512+
}
513+
instance.exports.bjs_roundTripOptionalJSValueArray(+isSome);
514+
const isSome1 = tmpRetInts.pop();
515+
let optResult;
516+
if (isSome1) {
517+
const arrayLen = tmpRetInts.pop();
518+
const arrayResult = [];
519+
for (let i = 0; i < arrayLen; i++) {
520+
const jsValuePayload2 = tmpRetF64s.pop();
521+
const jsValuePayload1 = tmpRetInts.pop();
522+
const jsValueKind = tmpRetInts.pop();
523+
const jsValue = __bjs_jsValueLift(jsValueKind, jsValuePayload1, jsValuePayload2);
524+
arrayResult.push(jsValue);
525+
}
526+
arrayResult.reverse();
527+
optResult = arrayResult;
528+
} else {
529+
optResult = null;
530+
}
531+
for (const cleanup of valuesCleanups) { cleanup(); }
532+
return optResult;
533+
},
451534
};
452535
_exports = exports;
453536
return exports;

0 commit comments

Comments
 (0)