Skip to content

Commit 2d7709c

Browse files
authored
Merge pull request #472 from PassiveLogic/fix/multiple-stack-based-parameters
BridgeJS: Fix ordering issue with multiple stack based parameters
2 parents 4b327dc + e68dcfb commit 2d7709c

File tree

10 files changed

+210
-21
lines changed

10 files changed

+210
-21
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,7 @@ public class ExportSwift {
16671667
}
16681668

16691669
func call(name: String, returnType: BridgeType) {
1670+
generateParameterLifting()
16701671
let item = renderCallStatement(callee: "\(raw: name)", returnType: returnType)
16711672
append(item)
16721673
}
@@ -1694,6 +1695,7 @@ public class ExportSwift {
16941695

16951696
func callMethod(klassName: String, methodName: String, returnType: BridgeType) {
16961697
let (_, selfExpr) = removeFirstLiftedParameter()
1698+
generateParameterLifting()
16971699
let item = renderCallStatement(
16981700
callee: "\(raw: selfExpr).\(raw: methodName)",
16991701
returnType: returnType
@@ -1827,6 +1829,29 @@ public class ExportSwift {
18271829
func returnSignature() -> String {
18281830
return abiReturnType?.swiftType ?? "Void"
18291831
}
1832+
1833+
/// Generates intermediate variables for stack-using parameters if needed for LIFO compatibility
1834+
private func generateParameterLifting() {
1835+
let stackParamIndices = parameters.enumerated().compactMap { index, param -> Int? in
1836+
switch param.type {
1837+
case .optional(.associatedValueEnum):
1838+
return index
1839+
default:
1840+
return nil
1841+
}
1842+
}
1843+
1844+
guard stackParamIndices.count > 1 else { return }
1845+
1846+
for index in stackParamIndices.reversed() {
1847+
let param = parameters[index]
1848+
let expr = liftedParameterExprs[index]
1849+
let varName = "_tmp_\(param.name)"
1850+
1851+
append("let \(raw: varName) = \(expr)")
1852+
liftedParameterExprs[index] = ExprSyntax(DeclReferenceExprSyntax(baseName: .identifier(varName)))
1853+
}
1854+
}
18301855
}
18311856

18321857
private struct ClosureCodegen {

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumAssociatedValue.swift

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@ enum APIResult {
1010

1111
@JS func handle(result: APIResult)
1212
@JS func getResult() -> APIResult
13-
@JS func roundtripAPIResult(result: APIResult) -> APIResult {
14-
return result
15-
}
16-
@JS func roundTripOptionalAPIResult(result: APIResult?) -> APIResult? {
17-
return result
18-
}
13+
@JS func roundtripAPIResult(result: APIResult) -> APIResult
14+
@JS func roundTripOptionalAPIResult(result: APIResult?) -> APIResult?
1915

2016
@JS
2117
enum ComplexResult {
@@ -29,12 +25,8 @@ enum ComplexResult {
2925

3026
@JS func handleComplex(result: ComplexResult)
3127
@JS func getComplexResult() -> ComplexResult
32-
@JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult {
33-
return result
34-
}
35-
@JS func roundTripOptionalComplexResult(result: ComplexResult?) -> ComplexResult? {
36-
return result
37-
}
28+
@JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult
29+
@JS func roundTripOptionalComplexResult(result: ComplexResult?) -> ComplexResult?
3830

3931
@JS
4032
enum Utilities {
@@ -45,26 +37,21 @@ enum Utilities {
4537
}
4638
}
4739

48-
@JS func roundTripOptionalUtilitiesResult(result: Utilities.Result?) -> Utilities.Result? {
49-
return result
50-
}
40+
@JS func roundTripOptionalUtilitiesResult(result: Utilities.Result?) -> Utilities.Result?
5141

5242
@JS(namespace: "API")
5343
@JS enum NetworkingResult {
5444
case success(String)
5545
case failure(String, Int)
5646
}
5747

58-
@JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult? {
59-
return result
60-
}
48+
@JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult?
6149

6250
@JS
6351
enum APIOptionalResult {
6452
case success(String?)
6553
case failure(Int?, Bool?)
6654
case status(Bool?, Int?, String?)
6755
}
68-
@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult? {
69-
return result
70-
}
56+
@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult?
57+
@JS func compareAPIResults(result1: APIOptionalResult?, result2: APIOptionalResult?) -> APIOptionalResult?

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export type Exports = {
8686
roundTripOptionalUtilitiesResult(result: Utilities.ResultTag | null): Utilities.ResultTag | null;
8787
roundTripOptionalNetworkingResult(result: API.NetworkingResultTag | null): API.NetworkingResultTag | null;
8888
roundTripOptionalAPIOptionalResult(result: APIOptionalResultTag | null): APIOptionalResultTag | null;
89+
compareAPIResults(result1: APIOptionalResultTag | null, result2: APIOptionalResultTag | null): APIOptionalResultTag | null;
8990
APIResult: APIResultObject
9091
ComplexResult: ComplexResultObject
9192
APIOptionalResult: APIOptionalResultObject

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,33 @@ export async function createInstantiator(options, swift) {
814814
if (resultCleanup) { resultCleanup(); }
815815
return optResult;
816816
},
817+
compareAPIResults: function bjs_compareAPIResults(result1, result2) {
818+
const isSome = result1 != null;
819+
let result1CaseId, result1Cleanup;
820+
if (isSome) {
821+
const enumResult = enumHelpers.APIOptionalResult.lower(result1);
822+
result1CaseId = enumResult.caseId;
823+
result1Cleanup = enumResult.cleanup;
824+
}
825+
const isSome1 = result2 != null;
826+
let result2CaseId, result2Cleanup;
827+
if (isSome1) {
828+
const enumResult1 = enumHelpers.APIOptionalResult.lower(result2);
829+
result2CaseId = enumResult1.caseId;
830+
result2Cleanup = enumResult1.cleanup;
831+
}
832+
instance.exports.bjs_compareAPIResults(+isSome, isSome ? result1CaseId : 0, +isSome1, isSome1 ? result2CaseId : 0);
833+
const isNull = (tmpRetTag === -1);
834+
let optResult;
835+
if (isNull) {
836+
optResult = null;
837+
} else {
838+
optResult = enumHelpers.APIOptionalResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s);
839+
}
840+
if (result1Cleanup) { result1Cleanup(); }
841+
if (result2Cleanup) { result2Cleanup(); }
842+
return optResult;
843+
},
817844
APIResult: APIResultValues,
818845
ComplexResult: ComplexResultValues,
819846
APIOptionalResult: APIOptionalResultValues,

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,52 @@
787787
}
788788
}
789789
}
790+
},
791+
{
792+
"abiName" : "bjs_compareAPIResults",
793+
"effects" : {
794+
"isAsync" : false,
795+
"isStatic" : false,
796+
"isThrows" : false
797+
},
798+
"name" : "compareAPIResults",
799+
"parameters" : [
800+
{
801+
"label" : "result1",
802+
"name" : "result1",
803+
"type" : {
804+
"optional" : {
805+
"_0" : {
806+
"associatedValueEnum" : {
807+
"_0" : "APIOptionalResult"
808+
}
809+
}
810+
}
811+
}
812+
},
813+
{
814+
"label" : "result2",
815+
"name" : "result2",
816+
"type" : {
817+
"optional" : {
818+
"_0" : {
819+
"associatedValueEnum" : {
820+
"_0" : "APIOptionalResult"
821+
}
822+
}
823+
}
824+
}
825+
}
826+
],
827+
"returnType" : {
828+
"optional" : {
829+
"_0" : {
830+
"associatedValueEnum" : {
831+
"_0" : "APIOptionalResult"
832+
}
833+
}
834+
}
835+
}
790836
}
791837
],
792838
"moduleName" : "TestModule",

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,4 +606,17 @@ public func _bjs_roundTripOptionalAPIOptionalResult(resultIsSome: Int32, resultC
606606
#else
607607
fatalError("Only available on WebAssembly")
608608
#endif
609+
}
610+
611+
@_expose(wasm, "bjs_compareAPIResults")
612+
@_cdecl("bjs_compareAPIResults")
613+
public func _bjs_compareAPIResults(result1IsSome: Int32, result1CaseId: Int32, result2IsSome: Int32, result2CaseId: Int32) -> Void {
614+
#if arch(wasm32)
615+
let _tmp_result2 = Optional<APIOptionalResult>.bridgeJSLiftParameter(result2IsSome, result2CaseId)
616+
let _tmp_result1 = Optional<APIOptionalResult>.bridgeJSLiftParameter(result1IsSome, result1CaseId)
617+
let ret = compareAPIResults(result1: _tmp_result1, result2: _tmp_result2)
618+
return ret.bridgeJSLowerReturn()
619+
#else
620+
fatalError("Only available on WebAssembly")
621+
#endif
609622
}

Tests/BridgeJSRuntimeTests/ExportAPITests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,32 @@ typealias OptionalAge = Int?
528528
return value
529529
}
530530

531+
@JS func compareAPIResults(_ r1: APIResult?, _ r2: APIResult?) -> String {
532+
let r1Str: String
533+
switch r1 {
534+
case .none: r1Str = "nil"
535+
case .some(.success(let msg)): r1Str = "success:\(msg)"
536+
case .some(.failure(let code)): r1Str = "failure:\(code)"
537+
case .some(.info): r1Str = "info"
538+
case .some(.flag(let b)): r1Str = "flag:\(b)"
539+
case .some(.rate(let r)): r1Str = "rate:\(r)"
540+
case .some(.precise(let p)): r1Str = "precise:\(p)"
541+
}
542+
543+
let r2Str: String
544+
switch r2 {
545+
case .none: r2Str = "nil"
546+
case .some(.success(let msg)): r2Str = "success:\(msg)"
547+
case .some(.failure(let code)): r2Str = "failure:\(code)"
548+
case .some(.info): r2Str = "info"
549+
case .some(.flag(let b)): r2Str = "flag:\(b)"
550+
case .some(.rate(let r)): r2Str = "rate:\(r)"
551+
case .some(.precise(let p)): r2Str = "precise:\(p)"
552+
}
553+
554+
return "r1:\(r1Str),r2:\(r2Str)"
555+
}
556+
531557
@JS func roundTripOptionalComplexResult(_ result: ComplexResult?) -> ComplexResult? {
532558
return result
533559
}

Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3079,6 +3079,19 @@ public func _bjs_roundTripOptionalAPIResult(valueIsSome: Int32, valueCaseId: Int
30793079
#endif
30803080
}
30813081

3082+
@_expose(wasm, "bjs_compareAPIResults")
3083+
@_cdecl("bjs_compareAPIResults")
3084+
public func _bjs_compareAPIResults(r1IsSome: Int32, r1CaseId: Int32, r2IsSome: Int32, r2CaseId: Int32) -> Void {
3085+
#if arch(wasm32)
3086+
let _tmp_r2 = Optional<APIResult>.bridgeJSLiftParameter(r2IsSome, r2CaseId)
3087+
let _tmp_r1 = Optional<APIResult>.bridgeJSLiftParameter(r1IsSome, r1CaseId)
3088+
let ret = compareAPIResults(_: _tmp_r1, _: _tmp_r2)
3089+
return ret.bridgeJSLowerReturn()
3090+
#else
3091+
fatalError("Only available on WebAssembly")
3092+
#endif
3093+
}
3094+
30823095
@_expose(wasm, "bjs_roundTripOptionalComplexResult")
30833096
@_cdecl("bjs_roundTripOptionalComplexResult")
30843097
public func _bjs_roundTripOptionalComplexResult(resultIsSome: Int32, resultCaseId: Int32) -> Void {

Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6826,6 +6826,48 @@
68266826
}
68276827
}
68286828
},
6829+
{
6830+
"abiName" : "bjs_compareAPIResults",
6831+
"effects" : {
6832+
"isAsync" : false,
6833+
"isStatic" : false,
6834+
"isThrows" : false
6835+
},
6836+
"name" : "compareAPIResults",
6837+
"parameters" : [
6838+
{
6839+
"label" : "_",
6840+
"name" : "r1",
6841+
"type" : {
6842+
"optional" : {
6843+
"_0" : {
6844+
"associatedValueEnum" : {
6845+
"_0" : "APIResult"
6846+
}
6847+
}
6848+
}
6849+
}
6850+
},
6851+
{
6852+
"label" : "_",
6853+
"name" : "r2",
6854+
"type" : {
6855+
"optional" : {
6856+
"_0" : {
6857+
"associatedValueEnum" : {
6858+
"_0" : "APIResult"
6859+
}
6860+
}
6861+
}
6862+
}
6863+
}
6864+
],
6865+
"returnType" : {
6866+
"string" : {
6867+
6868+
}
6869+
}
6870+
},
68296871
{
68306872
"abiName" : "bjs_roundTripOptionalComplexResult",
68316873
"effects" : {

Tests/prelude.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,15 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) {
571571
assert.deepEqual(exports.roundTripOptionalAPIResult(p1), p1);
572572
assert.deepEqual(exports.roundTripOptionalComplexResult(cl1), cl1);
573573

574+
const apiSuccess = { tag: exports.APIResult.Tag.Success, param0: "test success" };
575+
const apiFailure = { tag: exports.APIResult.Tag.Failure, param0: 404 };
576+
const apiInfo = { tag: exports.APIResult.Tag.Info };
577+
578+
assert.equal(exports.compareAPIResults(apiSuccess, apiFailure), "r1:success:test success,r2:failure:404");
579+
assert.equal(exports.compareAPIResults(null, apiInfo), "r1:nil,r2:info");
580+
assert.equal(exports.compareAPIResults(apiFailure, null), "r1:failure:404,r2:nil");
581+
assert.equal(exports.compareAPIResults(null, null), "r1:nil,r2:nil");
582+
574583
const optionalGreeter = new exports.Greeter("Schrödinger");
575584
const optionalGreeter2 = exports.roundTripOptionalClass(optionalGreeter);
576585
assert.equal(optionalGreeter2?.greet() ?? "", "Hello, Schrödinger!");

0 commit comments

Comments
 (0)