Skip to content

Commit 4d57e98

Browse files
committed
BridgeJS: Lower imported optional stack parameters fully on the stack
An imported optional whose payload is stack-only ([T]?, [String: V]?, @js struct?) used a hybrid convention: the isSome flag crossed as a wasm i32 parameter while the payload was conditionally pushed onto the shared stacks. Optional returns and optional array elements of the same types already travel entirely on the stacks: payload first, then a 0/1 flag on the i32 stack. This lowers those parameters the same way. The Swift thunk pushes the payload (if some) followed by the flag, the wasm signature carries no argument for the parameter, and the JS handler pops the flag before conditionally lifting the payload, through the same fragment already used for optional returns and elements. The hybrid shape was the last parameter category that both passed a wasm argument and pushed stack data, which is what enabled the argument transposition fixed in swiftwasm#794. Every stack-touching parameter is now flagless and reverse-ordered, matching returns and elements. All other optional parameter ABIs (scalars, strings, JSObject, closures, enums, heap objects) are unchanged.
1 parent 83eae16 commit 4d57e98

12 files changed

Lines changed: 147 additions & 158 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,10 @@ extension BridgeType {
939939
throw BridgeJSCoreError("Namespace enums cannot be used as parameters")
940940
case .nullable(let wrappedType, _):
941941
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
942+
if wrappedInfo.loweredParameters.isEmpty {
943+
// Optional stack-only payloads carry their isSome flag on the stack too (same as returns).
944+
return LoweringParameterInfo(loweredParameters: [])
945+
}
942946
var params = [("isSome", WasmCoreType.i32)]
943947
params.append(contentsOf: wrappedInfo.loweredParameters)
944948
return LoweringParameterInfo(loweredParameters: params, useBorrowing: wrappedInfo.useBorrowing)

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -668,16 +668,14 @@ struct IntrinsicJSFragment: Sendable {
668668
)
669669
}
670670

671-
let innerFragment =
672-
if wrappedType.optionalParameterUsesStackABI {
673-
try stackLiftFragment(elementType: wrappedType)
674-
} else {
675-
try liftParameter(type: wrappedType, context: bridgeContext)
676-
}
671+
if wrappedType.optionalParameterUsesStackABI {
672+
// Flag and payload both travel on the stacks; same fragment as optional returns.
673+
return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind)
674+
}
677675
return compositeOptionalLiftParameter(
678676
wrappedType: wrappedType,
679677
kind: kind,
680-
innerFragment: innerFragment
678+
innerFragment: try liftParameter(type: wrappedType, context: bridgeContext)
681679
)
682680
}
683681

@@ -686,22 +684,14 @@ struct IntrinsicJSFragment: Sendable {
686684
kind: JSOptionalKind,
687685
innerFragment: IntrinsicJSFragment
688686
) -> IntrinsicJSFragment {
689-
let isStackConvention = wrappedType.optionalParameterUsesStackABI
690687
let absenceLiteral = kind.absenceLiteral
691688

692-
let outerParams: [String]
693-
if isStackConvention {
694-
outerParams = ["isSome"]
695-
} else {
696-
outerParams = ["isSome"] + innerFragment.parameters
697-
}
698-
699689
return IntrinsicJSFragment(
700-
parameters: outerParams,
690+
parameters: ["isSome"] + innerFragment.parameters,
701691
printCode: { arguments, context in
702692
let (scope, printer) = (context.scope, context.printer)
703693
let isSome = arguments[0]
704-
let innerArgs = isStackConvention ? [] : Array(arguments.dropFirst())
694+
let innerArgs = Array(arguments.dropFirst())
705695

706696
let bufferPrinter = CodeFragmentPrinter()
707697
let innerResults = try innerFragment.printCode(

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -614,20 +614,20 @@ func _$Promise_resolve_Sq10AsyncThemeO(_ promise: JSObject, _ value: Optional<As
614614
615615
#if arch(wasm32)
616616
@_extern(wasm, module: "bjs", name: "promise_resolve_TestModule_Sq10AsyncPointV")
617-
fileprivate func promise_resolve_TestModule_Sq10AsyncPointV_extern(_ promise: Int32, _ value: Int32) -> Void
617+
fileprivate func promise_resolve_TestModule_Sq10AsyncPointV_extern(_ promise: Int32) -> Void
618618
#else
619-
fileprivate func promise_resolve_TestModule_Sq10AsyncPointV_extern(_ promise: Int32, _ value: Int32) -> Void {
619+
fileprivate func promise_resolve_TestModule_Sq10AsyncPointV_extern(_ promise: Int32) -> Void {
620620
fatalError("Only available on WebAssembly")
621621
}
622622
#endif
623-
@inline(never) fileprivate func promise_resolve_TestModule_Sq10AsyncPointV(_ promise: Int32, _ value: Int32) -> Void {
624-
return promise_resolve_TestModule_Sq10AsyncPointV_extern(promise, value)
623+
@inline(never) fileprivate func promise_resolve_TestModule_Sq10AsyncPointV(_ promise: Int32) -> Void {
624+
return promise_resolve_TestModule_Sq10AsyncPointV_extern(promise)
625625
}
626626
627627
func _$Promise_resolve_Sq10AsyncPointV(_ promise: JSObject, _ value: Optional<AsyncPoint>) throws(JSException) -> Void {
628-
let valueIsSome = value.bridgeJSLowerParameter()
628+
let _ = value.bridgeJSLowerParameter()
629629
let promiseValue = promise.bridgeJSLowerParameter()
630-
promise_resolve_TestModule_Sq10AsyncPointV(promiseValue, valueIsSome)
630+
promise_resolve_TestModule_Sq10AsyncPointV(promiseValue)
631631
if let error = _swift_js_take_exception() { throw error }
632632
}
633633

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,20 @@ func _$logStrings(_ items: [String]) throws(JSException) -> Void {
4141

4242
#if arch(wasm32)
4343
@_extern(wasm, module: "TestModule", name: "bjs_optionalArrayThenArray")
44-
fileprivate func bjs_optionalArrayThenArray_extern(_ a: Int32) -> Int32
44+
fileprivate func bjs_optionalArrayThenArray_extern() -> Int32
4545
#else
46-
fileprivate func bjs_optionalArrayThenArray_extern(_ a: Int32) -> Int32 {
46+
fileprivate func bjs_optionalArrayThenArray_extern() -> Int32 {
4747
fatalError("Only available on WebAssembly")
4848
}
4949
#endif
50-
@inline(never) fileprivate func bjs_optionalArrayThenArray(_ a: Int32) -> Int32 {
51-
return bjs_optionalArrayThenArray_extern(a)
50+
@inline(never) fileprivate func bjs_optionalArrayThenArray() -> Int32 {
51+
return bjs_optionalArrayThenArray_extern()
5252
}
5353

5454
func _$optionalArrayThenArray(_ a: Optional<[Int]>, _ b: [Int]) throws(JSException) -> Int {
5555
let _ = b.bridgeJSLowerParameter()
56-
let aIsSome = a.bridgeJSLowerParameter()
57-
let ret = bjs_optionalArrayThenArray(aIsSome)
56+
let _ = a.bridgeJSLowerParameter()
57+
let ret = bjs_optionalArrayThenArray()
5858
if let error = _swift_js_take_exception() {
5959
throw error
6060
}
@@ -63,21 +63,21 @@ func _$optionalArrayThenArray(_ a: Optional<[Int]>, _ b: [Int]) throws(JSExcepti
6363

6464
#if arch(wasm32)
6565
@_extern(wasm, module: "TestModule", name: "bjs_borrowedStringAroundStackParams")
66-
fileprivate func bjs_borrowedStringAroundStackParams_extern(_ sBytes: Int32, _ sLength: Int32, _ a: Int32) -> Int32
66+
fileprivate func bjs_borrowedStringAroundStackParams_extern(_ sBytes: Int32, _ sLength: Int32) -> Int32
6767
#else
68-
fileprivate func bjs_borrowedStringAroundStackParams_extern(_ sBytes: Int32, _ sLength: Int32, _ a: Int32) -> Int32 {
68+
fileprivate func bjs_borrowedStringAroundStackParams_extern(_ sBytes: Int32, _ sLength: Int32) -> Int32 {
6969
fatalError("Only available on WebAssembly")
7070
}
7171
#endif
72-
@inline(never) fileprivate func bjs_borrowedStringAroundStackParams(_ sBytes: Int32, _ sLength: Int32, _ a: Int32) -> Int32 {
73-
return bjs_borrowedStringAroundStackParams_extern(sBytes, sLength, a)
72+
@inline(never) fileprivate func bjs_borrowedStringAroundStackParams(_ sBytes: Int32, _ sLength: Int32) -> Int32 {
73+
return bjs_borrowedStringAroundStackParams_extern(sBytes, sLength)
7474
}
7575

7676
func _$borrowedStringAroundStackParams(_ s: String, _ a: Optional<[Int]>, _ b: [Int]) throws(JSException) -> Int {
7777
let ret0 = s.bridgeJSWithLoweredParameter { (sBytes, sLength) in
7878
let _ = b.bridgeJSLowerParameter()
79-
let aIsSome = a.bridgeJSLowerParameter()
80-
let ret = bjs_borrowedStringAroundStackParams(sBytes, sLength, aIsSome)
79+
let _ = a.bridgeJSLowerParameter()
80+
let ret = bjs_borrowedStringAroundStackParams(sBytes, sLength)
8181
return ret
8282
}
8383
let ret = ret0

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -992,14 +992,14 @@ public func _invoke_swift_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO(_ b
992992

993993
#if arch(wasm32)
994994
@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV")
995-
fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32, _ param0: Int32) -> Void
995+
fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32) -> Void
996996
#else
997-
fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32, _ param0: Int32) -> Void {
997+
fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(_ callback: Int32) -> Void {
998998
fatalError("Only available on WebAssembly")
999999
}
10001000
#endif
1001-
@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(_ callback: Int32, _ param0: Int32) -> Void {
1002-
return invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(callback, param0)
1001+
@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(_ callback: Int32) -> Void {
1002+
return invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV_extern(callback)
10031003
}
10041004

10051005
#if arch(wasm32)
@@ -1019,9 +1019,9 @@ private enum _BJS_Closure_10TestModuleSq6AnimalV_Sq6AnimalV {
10191019
let callback = JSObject.bridgeJSLiftParameter(callbackId)
10201020
return { [callback] param0 in
10211021
#if arch(wasm32)
1022-
let param0IsSome = param0.bridgeJSLowerParameter()
1022+
let _ = param0.bridgeJSLowerParameter()
10231023
let callbackValue = callback.bridgeJSLowerParameter()
1024-
invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(callbackValue, param0IsSome)
1024+
invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV(callbackValue)
10251025
return Optional<Animal>.bridgeJSLiftReturn()
10261026
#else
10271027
fatalError("Only available on WebAssembly")

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,19 @@ func _$translate(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException) -> Po
7171

7272
#if arch(wasm32)
7373
@_extern(wasm, module: "TestModule", name: "bjs_roundTripOptional")
74-
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void
74+
fileprivate func bjs_roundTripOptional_extern() -> Void
7575
#else
76-
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void {
76+
fileprivate func bjs_roundTripOptional_extern() -> Void {
7777
fatalError("Only available on WebAssembly")
7878
}
7979
#endif
80-
@inline(never) fileprivate func bjs_roundTripOptional(_ point: Int32) -> Void {
81-
return bjs_roundTripOptional_extern(point)
80+
@inline(never) fileprivate func bjs_roundTripOptional() -> Void {
81+
return bjs_roundTripOptional_extern()
8282
}
8383

8484
func _$roundTripOptional(_ point: Optional<Point>) throws(JSException) -> Optional<Point> {
85-
let pointIsSome = point.bridgeJSLowerParameter()
86-
bjs_roundTripOptional(pointIsSome)
85+
let _ = point.bridgeJSLowerParameter()
86+
bjs_roundTripOptional()
8787
if let error = _swift_js_take_exception() {
8888
throw error
8989
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,17 @@ export async function createInstantiator(options, swift) {
324324
setException(error);
325325
}
326326
}
327-
bjs["promise_resolve_TestModule_Sq10AsyncPointV"] = function(promise, value) {
327+
bjs["promise_resolve_TestModule_Sq10AsyncPointV"] = function(promise) {
328328
try {
329-
let optResult;
330-
if (value) {
331-
const struct = structHelpers.AsyncPoint.lift();
332-
optResult = struct;
329+
const isSome = i32Stack.pop();
330+
let optValue;
331+
if (isSome === 0) {
332+
optValue = null;
333333
} else {
334-
optResult = null;
334+
const struct = structHelpers.AsyncPoint.lift();
335+
optValue = struct;
335336
}
336-
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(optResult);
337+
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(optValue);
337338
} catch (error) {
338339
setException(error);
339340
}

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,13 @@ export async function createInstantiator(options, swift) {
247247
setException(error);
248248
}
249249
}
250-
TestModule["bjs_optionalArrayThenArray"] = function bjs_optionalArrayThenArray(a) {
250+
TestModule["bjs_optionalArrayThenArray"] = function bjs_optionalArrayThenArray() {
251251
try {
252-
let optResult;
253-
if (a) {
252+
const isSome = i32Stack.pop();
253+
let optValue;
254+
if (isSome === 0) {
255+
optValue = null;
256+
} else {
254257
const arrayLen = i32Stack.pop();
255258
let arrayResult;
256259
if (arrayLen === -1) {
@@ -263,9 +266,7 @@ export async function createInstantiator(options, swift) {
263266
}
264267
arrayResult.reverse();
265268
}
266-
optResult = arrayResult;
267-
} else {
268-
optResult = null;
269+
optValue = arrayResult;
269270
}
270271
const arrayLen1 = i32Stack.pop();
271272
let arrayResult1;
@@ -279,18 +280,21 @@ export async function createInstantiator(options, swift) {
279280
}
280281
arrayResult1.reverse();
281282
}
282-
let ret = imports.optionalArrayThenArray(optResult, arrayResult1);
283+
let ret = imports.optionalArrayThenArray(optValue, arrayResult1);
283284
return ret;
284285
} catch (error) {
285286
setException(error);
286287
return 0
287288
}
288289
}
289-
TestModule["bjs_borrowedStringAroundStackParams"] = function bjs_borrowedStringAroundStackParams(sBytes, sCount, a) {
290+
TestModule["bjs_borrowedStringAroundStackParams"] = function bjs_borrowedStringAroundStackParams(sBytes, sCount) {
290291
try {
291292
const string = decodeString(sBytes, sCount);
292-
let optResult;
293-
if (a) {
293+
const isSome = i32Stack.pop();
294+
let optValue;
295+
if (isSome === 0) {
296+
optValue = null;
297+
} else {
294298
const arrayLen = i32Stack.pop();
295299
let arrayResult;
296300
if (arrayLen === -1) {
@@ -303,9 +307,7 @@ export async function createInstantiator(options, swift) {
303307
}
304308
arrayResult.reverse();
305309
}
306-
optResult = arrayResult;
307-
} else {
308-
optResult = null;
310+
optValue = arrayResult;
309311
}
310312
const arrayLen1 = i32Stack.pop();
311313
let arrayResult1;
@@ -319,7 +321,7 @@ export async function createInstantiator(options, swift) {
319321
}
320322
arrayResult1.reverse();
321323
}
322-
let ret = imports.borrowedStringAroundStackParams(string, optResult, arrayResult1);
324+
let ret = imports.borrowedStringAroundStackParams(string, optValue, arrayResult1);
323325
return ret;
324326
} catch (error) {
325327
setException(error);

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -852,22 +852,23 @@ export async function createInstantiator(options, swift) {
852852
};
853853
return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModuleSq5ThemeO_Sq5ThemeO);
854854
}
855-
bjs["invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV"] = function(callbackId, param0) {
855+
bjs["invoke_js_callback_TestModule_10TestModuleSq6AnimalV_Sq6AnimalV"] = function(callbackId) {
856856
try {
857857
const callback = swift.memory.getObject(callbackId);
858-
let optResult;
859-
if (param0) {
860-
const struct = structHelpers.Animal.lift();
861-
optResult = struct;
858+
const isSome = i32Stack.pop();
859+
let optValue;
860+
if (isSome === 0) {
861+
optValue = null;
862862
} else {
863-
optResult = null;
863+
const struct = structHelpers.Animal.lift();
864+
optValue = struct;
864865
}
865-
let ret = callback(optResult);
866-
const isSome = ret != null;
867-
if (isSome) {
866+
let ret = callback(optValue);
867+
const isSome1 = ret != null;
868+
if (isSome1) {
868869
structHelpers.Animal.lower(ret);
869870
}
870-
i32Stack.push(isSome ? 1 : 0);
871+
i32Stack.push(isSome1 ? 1 : 0);
871872
} catch (error) {
872873
setException(error);
873874
}

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -232,21 +232,22 @@ export async function createInstantiator(options, swift) {
232232
setException(error);
233233
}
234234
}
235-
TestModule["bjs_roundTripOptional"] = function bjs_roundTripOptional(point) {
235+
TestModule["bjs_roundTripOptional"] = function bjs_roundTripOptional() {
236236
try {
237-
let optResult;
238-
if (point) {
239-
const struct = structHelpers.Point.lift();
240-
optResult = struct;
237+
const isSome = i32Stack.pop();
238+
let optValue;
239+
if (isSome === 0) {
240+
optValue = null;
241241
} else {
242-
optResult = null;
242+
const struct = structHelpers.Point.lift();
243+
optValue = struct;
243244
}
244-
let ret = imports.roundTripOptional(optResult);
245-
const isSome = ret != null;
246-
if (isSome) {
245+
let ret = imports.roundTripOptional(optValue);
246+
const isSome1 = ret != null;
247+
if (isSome1) {
247248
structHelpers.Point.lower(ret);
248249
}
249-
i32Stack.push(isSome ? 1 : 0);
250+
i32Stack.push(isSome1 ? 1 : 0);
250251
} catch (error) {
251252
setException(error);
252253
}

0 commit comments

Comments
 (0)