Skip to content

Commit 158b64e

Browse files
committed
BridgeJS: Fix stack ordering for multiple stack-based parameters
1 parent 44d9a94 commit 158b64e

File tree

10 files changed

+681
-9
lines changed

10 files changed

+681
-9
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,17 +243,16 @@ public class ExportSwift {
243243
append(item)
244244
}
245245

246-
/// Generates intermediate variables for stack-using parameters if needed for LIFO compatibility
246+
/// Generates intermediate variables for stack-using parameters if needed for LIFO compatibility.
247+
///
248+
/// When multiple stack-based parameters appear in a single function, JS pushes
249+
/// their data in forward declaration order onto shared LIFO stacks. Swift evaluates
250+
/// arguments left-to-right, popping from those stacks. Without intervention, the
251+
/// first parameter would pop the last parameter's data. This method extracts stack
252+
/// parameter pops into reverse-ordered temporaries so each parameter reads its own data.
247253
private func generateParameterLifting() {
248254
let stackParamIndices = parameters.enumerated().compactMap { index, param -> Int? in
249-
switch param.type {
250-
case .swiftStruct, .nullable(.swiftStruct, _),
251-
.associatedValueEnum, .nullable(.associatedValueEnum, _),
252-
.array:
253-
return index
254-
default:
255-
return nil
256-
}
255+
param.type.isStackUsingParameter ? index : nil
257256
}
258257

259258
guard stackParamIndices.count > 1 else { return }
@@ -1547,6 +1546,17 @@ extension BridgeType {
15471546
return false
15481547
}
15491548

1549+
var isStackUsingParameter: Bool {
1550+
switch self {
1551+
case .swiftStruct, .array, .dictionary, .associatedValueEnum:
1552+
return true
1553+
case .nullable(let wrapped, _):
1554+
return wrapped.isStackUsingParameter
1555+
default:
1556+
return false
1557+
}
1558+
}
1559+
15501560
struct LiftingIntrinsicInfo: Sendable {
15511561
let parameters: [(name: String, type: WasmCoreType)]
15521562

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,19 @@
6767
@JSFunction func importTransformNumbers(_ values: [Double]) throws(JSException) -> [Double]
6868
@JSFunction func importProcessStrings(_ values: [String]) throws(JSException) -> [String]
6969
@JSFunction func importProcessBooleans(_ values: [Bool]) throws(JSException) -> [Bool]
70+
71+
@JS func multiArrayParams(nums: [Int], strs: [String]) -> Int
72+
@JS func multiOptionalArrayParams(a: [Int]?, b: [String]?) -> Int
73+
74+
@JS class MultiArrayContainer {
75+
var nums: [Int]
76+
var strs: [String]
77+
78+
@JS init(nums: [Int], strs: [String]) {
79+
self.nums = nums
80+
self.strs = strs
81+
}
82+
83+
@JS var numbers: [Int] { nums }
84+
@JS var strings: [String] { strs }
85+
}

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

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,79 @@
1010

1111
],
1212
"swiftCallName" : "Item"
13+
},
14+
{
15+
"constructor" : {
16+
"abiName" : "bjs_MultiArrayContainer_init",
17+
"effects" : {
18+
"isAsync" : false,
19+
"isStatic" : false,
20+
"isThrows" : false
21+
},
22+
"parameters" : [
23+
{
24+
"label" : "nums",
25+
"name" : "nums",
26+
"type" : {
27+
"array" : {
28+
"_0" : {
29+
"int" : {
30+
31+
}
32+
}
33+
}
34+
}
35+
},
36+
{
37+
"label" : "strs",
38+
"name" : "strs",
39+
"type" : {
40+
"array" : {
41+
"_0" : {
42+
"string" : {
43+
44+
}
45+
}
46+
}
47+
}
48+
}
49+
]
50+
},
51+
"methods" : [
52+
53+
],
54+
"name" : "MultiArrayContainer",
55+
"properties" : [
56+
{
57+
"isReadonly" : true,
58+
"isStatic" : false,
59+
"name" : "numbers",
60+
"type" : {
61+
"array" : {
62+
"_0" : {
63+
"int" : {
64+
65+
}
66+
}
67+
}
68+
}
69+
},
70+
{
71+
"isReadonly" : true,
72+
"isStatic" : false,
73+
"name" : "strings",
74+
"type" : {
75+
"array" : {
76+
"_0" : {
77+
"string" : {
78+
79+
}
80+
}
81+
}
82+
}
83+
}
84+
],
85+
"swiftCallName" : "MultiArrayContainer"
1386
}
1487
],
1588
"enums" : [
@@ -1074,6 +1147,100 @@
10741147
}
10751148
}
10761149
}
1150+
},
1151+
{
1152+
"abiName" : "bjs_multiArrayParams",
1153+
"effects" : {
1154+
"isAsync" : false,
1155+
"isStatic" : false,
1156+
"isThrows" : false
1157+
},
1158+
"name" : "multiArrayParams",
1159+
"parameters" : [
1160+
{
1161+
"label" : "nums",
1162+
"name" : "nums",
1163+
"type" : {
1164+
"array" : {
1165+
"_0" : {
1166+
"int" : {
1167+
1168+
}
1169+
}
1170+
}
1171+
}
1172+
},
1173+
{
1174+
"label" : "strs",
1175+
"name" : "strs",
1176+
"type" : {
1177+
"array" : {
1178+
"_0" : {
1179+
"string" : {
1180+
1181+
}
1182+
}
1183+
}
1184+
}
1185+
}
1186+
],
1187+
"returnType" : {
1188+
"int" : {
1189+
1190+
}
1191+
}
1192+
},
1193+
{
1194+
"abiName" : "bjs_multiOptionalArrayParams",
1195+
"effects" : {
1196+
"isAsync" : false,
1197+
"isStatic" : false,
1198+
"isThrows" : false
1199+
},
1200+
"name" : "multiOptionalArrayParams",
1201+
"parameters" : [
1202+
{
1203+
"label" : "a",
1204+
"name" : "a",
1205+
"type" : {
1206+
"nullable" : {
1207+
"_0" : {
1208+
"array" : {
1209+
"_0" : {
1210+
"int" : {
1211+
1212+
}
1213+
}
1214+
}
1215+
},
1216+
"_1" : "null"
1217+
}
1218+
}
1219+
},
1220+
{
1221+
"label" : "b",
1222+
"name" : "b",
1223+
"type" : {
1224+
"nullable" : {
1225+
"_0" : {
1226+
"array" : {
1227+
"_0" : {
1228+
"string" : {
1229+
1230+
}
1231+
}
1232+
}
1233+
},
1234+
"_1" : "null"
1235+
}
1236+
}
1237+
}
1238+
],
1239+
"returnType" : {
1240+
"int" : {
1241+
1242+
}
1243+
}
10771244
}
10781245
],
10791246
"protocols" : [

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,32 @@ public func _bjs_processNestedJSObjectArray() -> Void {
470470
#endif
471471
}
472472

473+
@_expose(wasm, "bjs_multiArrayParams")
474+
@_cdecl("bjs_multiArrayParams")
475+
public func _bjs_multiArrayParams() -> Int32 {
476+
#if arch(wasm32)
477+
let _tmp_strs = [String].bridgeJSStackPop()
478+
let _tmp_nums = [Int].bridgeJSStackPop()
479+
let ret = multiArrayParams(nums: _tmp_nums, strs: _tmp_strs)
480+
return ret.bridgeJSLowerReturn()
481+
#else
482+
fatalError("Only available on WebAssembly")
483+
#endif
484+
}
485+
486+
@_expose(wasm, "bjs_multiOptionalArrayParams")
487+
@_cdecl("bjs_multiOptionalArrayParams")
488+
public func _bjs_multiOptionalArrayParams() -> Int32 {
489+
#if arch(wasm32)
490+
let _tmp_b = Optional<[String]>.bridgeJSLiftParameter()
491+
let _tmp_a = Optional<[Int]>.bridgeJSLiftParameter()
492+
let ret = multiOptionalArrayParams(a: _tmp_a, b: _tmp_b)
493+
return ret.bridgeJSLowerReturn()
494+
#else
495+
fatalError("Only available on WebAssembly")
496+
#endif
497+
}
498+
473499
@_expose(wasm, "bjs_Item_deinit")
474500
@_cdecl("bjs_Item_deinit")
475501
public func _bjs_Item_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
@@ -498,6 +524,69 @@ fileprivate func _bjs_Item_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> In
498524
return _bjs_Item_wrap_extern(pointer)
499525
}
500526

527+
@_expose(wasm, "bjs_MultiArrayContainer_init")
528+
@_cdecl("bjs_MultiArrayContainer_init")
529+
public func _bjs_MultiArrayContainer_init() -> UnsafeMutableRawPointer {
530+
#if arch(wasm32)
531+
let _tmp_strs = [String].bridgeJSStackPop()
532+
let _tmp_nums = [Int].bridgeJSStackPop()
533+
let ret = MultiArrayContainer(nums: _tmp_nums, strs: _tmp_strs)
534+
return ret.bridgeJSLowerReturn()
535+
#else
536+
fatalError("Only available on WebAssembly")
537+
#endif
538+
}
539+
540+
@_expose(wasm, "bjs_MultiArrayContainer_numbers_get")
541+
@_cdecl("bjs_MultiArrayContainer_numbers_get")
542+
public func _bjs_MultiArrayContainer_numbers_get(_ _self: UnsafeMutableRawPointer) -> Void {
543+
#if arch(wasm32)
544+
let ret = MultiArrayContainer.bridgeJSLiftParameter(_self).numbers
545+
ret.bridgeJSStackPush()
546+
#else
547+
fatalError("Only available on WebAssembly")
548+
#endif
549+
}
550+
551+
@_expose(wasm, "bjs_MultiArrayContainer_strings_get")
552+
@_cdecl("bjs_MultiArrayContainer_strings_get")
553+
public func _bjs_MultiArrayContainer_strings_get(_ _self: UnsafeMutableRawPointer) -> Void {
554+
#if arch(wasm32)
555+
let ret = MultiArrayContainer.bridgeJSLiftParameter(_self).strings
556+
ret.bridgeJSStackPush()
557+
#else
558+
fatalError("Only available on WebAssembly")
559+
#endif
560+
}
561+
562+
@_expose(wasm, "bjs_MultiArrayContainer_deinit")
563+
@_cdecl("bjs_MultiArrayContainer_deinit")
564+
public func _bjs_MultiArrayContainer_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
565+
#if arch(wasm32)
566+
Unmanaged<MultiArrayContainer>.fromOpaque(pointer).release()
567+
#else
568+
fatalError("Only available on WebAssembly")
569+
#endif
570+
}
571+
572+
extension MultiArrayContainer: ConvertibleToJSValue, _BridgedSwiftHeapObject {
573+
var jsValue: JSValue {
574+
return .object(JSObject(id: UInt32(bitPattern: _bjs_MultiArrayContainer_wrap(Unmanaged.passRetained(self).toOpaque()))))
575+
}
576+
}
577+
578+
#if arch(wasm32)
579+
@_extern(wasm, module: "TestModule", name: "bjs_MultiArrayContainer_wrap")
580+
fileprivate func _bjs_MultiArrayContainer_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32
581+
#else
582+
fileprivate func _bjs_MultiArrayContainer_wrap_extern(_ pointer: UnsafeMutableRawPointer) -> Int32 {
583+
fatalError("Only available on WebAssembly")
584+
}
585+
#endif
586+
@inline(never) fileprivate func _bjs_MultiArrayContainer_wrap(_ pointer: UnsafeMutableRawPointer) -> Int32 {
587+
return _bjs_MultiArrayContainer_wrap_extern(pointer)
588+
}
589+
501590
#if arch(wasm32)
502591
@_extern(wasm, module: "TestModule", name: "bjs_checkArray")
503592
fileprivate func bjs_checkArray_extern(_ a: Int32) -> Void

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,16 @@ export interface SwiftHeapObject {
3636
}
3737
export interface Item extends SwiftHeapObject {
3838
}
39+
export interface MultiArrayContainer extends SwiftHeapObject {
40+
readonly numbers: number[];
41+
readonly strings: string[];
42+
}
3943
export type Exports = {
4044
Item: {
4145
}
46+
MultiArrayContainer: {
47+
new(nums: number[], strs: string[]): MultiArrayContainer;
48+
}
4249
processIntArray(values: number[]): number[];
4350
processStringArray(values: string[]): string[];
4451
processDoubleArray(values: number[]): number[];
@@ -65,6 +72,8 @@ export type Exports = {
6572
processJSObjectArray(objects: any[]): any[];
6673
processOptionalJSObjectArray(objects: (any | null)[]): (any | null)[];
6774
processNestedJSObjectArray(objects: any[][]): any[][];
75+
multiArrayParams(nums: number[], strs: string[]): number;
76+
multiOptionalArrayParams(a: number[] | null, b: string[] | null): number;
6877
Direction: DirectionObject
6978
Status: StatusObject
7079
}

0 commit comments

Comments
 (0)