Skip to content

Commit 3f508a1

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 3f508a1

12 files changed

Lines changed: 143 additions & 174 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,9 @@ 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+
return LoweringParameterInfo(loweredParameters: [])
944+
}
942945
var params = [("isSome", WasmCoreType.i32)]
943946
params.append(contentsOf: wrappedInfo.loweredParameters)
944947
return LoweringParameterInfo(loweredParameters: params, useBorrowing: wrappedInfo.useBorrowing)

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -668,16 +668,13 @@ 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+
return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind)
673+
}
677674
return compositeOptionalLiftParameter(
678675
wrappedType: wrappedType,
679676
kind: kind,
680-
innerFragment: innerFragment
677+
innerFragment: try liftParameter(type: wrappedType, context: bridgeContext)
681678
)
682679
}
683680

@@ -686,22 +683,14 @@ struct IntrinsicJSFragment: Sendable {
686683
kind: JSOptionalKind,
687684
innerFragment: IntrinsicJSFragment
688685
) -> IntrinsicJSFragment {
689-
let isStackConvention = wrappedType.optionalParameterUsesStackABI
690686
let absenceLiteral = kind.absenceLiteral
691687

692-
let outerParams: [String]
693-
if isStackConvention {
694-
outerParams = ["isSome"]
695-
} else {
696-
outerParams = ["isSome"] + innerFragment.parameters
697-
}
698-
699688
return IntrinsicJSFragment(
700-
parameters: outerParams,
689+
parameters: ["isSome"] + innerFragment.parameters,
701690
printCode: { arguments, context in
702691
let (scope, printer) = (context.scope, context.printer)
703692
let isSome = arguments[0]
704-
let innerArgs = isStackConvention ? [] : Array(arguments.dropFirst())
693+
let innerArgs = Array(arguments.dropFirst())
705694

706695
let bufferPrinter = CodeFragmentPrinter()
707696
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)