Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,7 @@ public class ExportSwift {
}

func call(name: String, returnType: BridgeType) {
generateParameterLifting()
let item = renderCallStatement(callee: "\(raw: name)", returnType: returnType)
append(item)
}
Expand Down Expand Up @@ -1694,6 +1695,7 @@ public class ExportSwift {

func callMethod(klassName: String, methodName: String, returnType: BridgeType) {
let (_, selfExpr) = removeFirstLiftedParameter()
generateParameterLifting()
let item = renderCallStatement(
callee: "\(raw: selfExpr).\(raw: methodName)",
returnType: returnType
Expand Down Expand Up @@ -1827,6 +1829,29 @@ public class ExportSwift {
func returnSignature() -> String {
return abiReturnType?.swiftType ?? "Void"
}

/// Generates intermediate variables for stack-using parameters if needed for LIFO compatibility
private func generateParameterLifting() {
let stackParamIndices = parameters.enumerated().compactMap { index, param -> Int? in
switch param.type {
case .optional(.associatedValueEnum):
return index
default:
return nil
}
}

guard stackParamIndices.count > 1 else { return }

for index in stackParamIndices.reversed() {
let param = parameters[index]
let expr = liftedParameterExprs[index]
let varName = "_tmp_\(param.name)"

append("let \(raw: varName) = \(expr)")
liftedParameterExprs[index] = ExprSyntax(DeclReferenceExprSyntax(baseName: .identifier(varName)))
}
}
}

private struct ClosureCodegen {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ enum APIResult {

@JS func handle(result: APIResult)
@JS func getResult() -> APIResult
@JS func roundtripAPIResult(result: APIResult) -> APIResult {
return result
}
@JS func roundTripOptionalAPIResult(result: APIResult?) -> APIResult? {
return result
}
@JS func roundtripAPIResult(result: APIResult) -> APIResult
@JS func roundTripOptionalAPIResult(result: APIResult?) -> APIResult?

@JS
enum ComplexResult {
Expand All @@ -29,12 +25,8 @@ enum ComplexResult {

@JS func handleComplex(result: ComplexResult)
@JS func getComplexResult() -> ComplexResult
@JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult {
return result
}
@JS func roundTripOptionalComplexResult(result: ComplexResult?) -> ComplexResult? {
return result
}
@JS func roundtripComplexResult(_ result: ComplexResult) -> ComplexResult
@JS func roundTripOptionalComplexResult(result: ComplexResult?) -> ComplexResult?

@JS
enum Utilities {
Expand All @@ -45,26 +37,21 @@ enum Utilities {
}
}

@JS func roundTripOptionalUtilitiesResult(result: Utilities.Result?) -> Utilities.Result? {
return result
}
@JS func roundTripOptionalUtilitiesResult(result: Utilities.Result?) -> Utilities.Result?

@JS(namespace: "API")
@JS enum NetworkingResult {
case success(String)
case failure(String, Int)
}

@JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult? {
return result
}
@JS func roundTripOptionalNetworkingResult(result: NetworkingResult?) -> NetworkingResult?

@JS
enum APIOptionalResult {
case success(String?)
case failure(Int?, Bool?)
case status(Bool?, Int?, String?)
}
@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult? {
return result
}
@JS func roundTripOptionalAPIOptionalResult(result: APIOptionalResult?) -> APIOptionalResult?
@JS func compareAPIResults(result1: APIOptionalResult?, result2: APIOptionalResult?) -> APIOptionalResult?
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type Exports = {
roundTripOptionalUtilitiesResult(result: Utilities.ResultTag | null): Utilities.ResultTag | null;
roundTripOptionalNetworkingResult(result: API.NetworkingResultTag | null): API.NetworkingResultTag | null;
roundTripOptionalAPIOptionalResult(result: APIOptionalResultTag | null): APIOptionalResultTag | null;
compareAPIResults(result1: APIOptionalResultTag | null, result2: APIOptionalResultTag | null): APIOptionalResultTag | null;
APIResult: APIResultObject
ComplexResult: ComplexResultObject
APIOptionalResult: APIOptionalResultObject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,33 @@ export async function createInstantiator(options, swift) {
if (resultCleanup) { resultCleanup(); }
return optResult;
},
compareAPIResults: function bjs_compareAPIResults(result1, result2) {
const isSome = result1 != null;
let result1CaseId, result1Cleanup;
if (isSome) {
const enumResult = enumHelpers.APIOptionalResult.lower(result1);
result1CaseId = enumResult.caseId;
result1Cleanup = enumResult.cleanup;
}
const isSome1 = result2 != null;
let result2CaseId, result2Cleanup;
if (isSome1) {
const enumResult1 = enumHelpers.APIOptionalResult.lower(result2);
result2CaseId = enumResult1.caseId;
result2Cleanup = enumResult1.cleanup;
}
instance.exports.bjs_compareAPIResults(+isSome, isSome ? result1CaseId : 0, +isSome1, isSome1 ? result2CaseId : 0);
const isNull = (tmpRetTag === -1);
let optResult;
if (isNull) {
optResult = null;
} else {
optResult = enumHelpers.APIOptionalResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s);
}
if (result1Cleanup) { result1Cleanup(); }
if (result2Cleanup) { result2Cleanup(); }
return optResult;
},
APIResult: APIResultValues,
ComplexResult: ComplexResultValues,
APIOptionalResult: APIOptionalResultValues,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,52 @@
}
}
}
},
{
"abiName" : "bjs_compareAPIResults",
"effects" : {
"isAsync" : false,
"isStatic" : false,
"isThrows" : false
},
"name" : "compareAPIResults",
"parameters" : [
{
"label" : "result1",
"name" : "result1",
"type" : {
"optional" : {
"_0" : {
"associatedValueEnum" : {
"_0" : "APIOptionalResult"
}
}
}
}
},
{
"label" : "result2",
"name" : "result2",
"type" : {
"optional" : {
"_0" : {
"associatedValueEnum" : {
"_0" : "APIOptionalResult"
}
}
}
}
}
],
"returnType" : {
"optional" : {
"_0" : {
"associatedValueEnum" : {
"_0" : "APIOptionalResult"
}
}
}
}
}
],
"moduleName" : "TestModule",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,4 +606,17 @@ public func _bjs_roundTripOptionalAPIOptionalResult(resultIsSome: Int32, resultC
#else
fatalError("Only available on WebAssembly")
#endif
}

@_expose(wasm, "bjs_compareAPIResults")
@_cdecl("bjs_compareAPIResults")
public func _bjs_compareAPIResults(result1IsSome: Int32, result1CaseId: Int32, result2IsSome: Int32, result2CaseId: Int32) -> Void {
#if arch(wasm32)
let _tmp_result2 = Optional<APIOptionalResult>.bridgeJSLiftParameter(result2IsSome, result2CaseId)
let _tmp_result1 = Optional<APIOptionalResult>.bridgeJSLiftParameter(result1IsSome, result1CaseId)
let ret = compareAPIResults(result1: _tmp_result1, result2: _tmp_result2)
return ret.bridgeJSLowerReturn()
#else
fatalError("Only available on WebAssembly")
#endif
}
26 changes: 26 additions & 0 deletions Tests/BridgeJSRuntimeTests/ExportAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,32 @@ typealias OptionalAge = Int?
return value
}

@JS func compareAPIResults(_ r1: APIResult?, _ r2: APIResult?) -> String {
let r1Str: String
switch r1 {
case .none: r1Str = "nil"
case .some(.success(let msg)): r1Str = "success:\(msg)"
case .some(.failure(let code)): r1Str = "failure:\(code)"
case .some(.info): r1Str = "info"
case .some(.flag(let b)): r1Str = "flag:\(b)"
case .some(.rate(let r)): r1Str = "rate:\(r)"
case .some(.precise(let p)): r1Str = "precise:\(p)"
}

let r2Str: String
switch r2 {
case .none: r2Str = "nil"
case .some(.success(let msg)): r2Str = "success:\(msg)"
case .some(.failure(let code)): r2Str = "failure:\(code)"
case .some(.info): r2Str = "info"
case .some(.flag(let b)): r2Str = "flag:\(b)"
case .some(.rate(let r)): r2Str = "rate:\(r)"
case .some(.precise(let p)): r2Str = "precise:\(p)"
}

return "r1:\(r1Str),r2:\(r2Str)"
}

@JS func roundTripOptionalComplexResult(_ result: ComplexResult?) -> ComplexResult? {
return result
}
Expand Down
13 changes: 13 additions & 0 deletions Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,19 @@ public func _bjs_roundTripOptionalAPIResult(valueIsSome: Int32, valueCaseId: Int
#endif
}

@_expose(wasm, "bjs_compareAPIResults")
@_cdecl("bjs_compareAPIResults")
public func _bjs_compareAPIResults(r1IsSome: Int32, r1CaseId: Int32, r2IsSome: Int32, r2CaseId: Int32) -> Void {
#if arch(wasm32)
let _tmp_r2 = Optional<APIResult>.bridgeJSLiftParameter(r2IsSome, r2CaseId)
let _tmp_r1 = Optional<APIResult>.bridgeJSLiftParameter(r1IsSome, r1CaseId)
let ret = compareAPIResults(_: _tmp_r1, _: _tmp_r2)
return ret.bridgeJSLowerReturn()
#else
fatalError("Only available on WebAssembly")
#endif
}

@_expose(wasm, "bjs_roundTripOptionalComplexResult")
@_cdecl("bjs_roundTripOptionalComplexResult")
public func _bjs_roundTripOptionalComplexResult(resultIsSome: Int32, resultCaseId: Int32) -> Void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6826,6 +6826,48 @@
}
}
},
{
"abiName" : "bjs_compareAPIResults",
"effects" : {
"isAsync" : false,
"isStatic" : false,
"isThrows" : false
},
"name" : "compareAPIResults",
"parameters" : [
{
"label" : "_",
"name" : "r1",
"type" : {
"optional" : {
"_0" : {
"associatedValueEnum" : {
"_0" : "APIResult"
}
}
}
}
},
{
"label" : "_",
"name" : "r2",
"type" : {
"optional" : {
"_0" : {
"associatedValueEnum" : {
"_0" : "APIResult"
}
}
}
}
}
],
"returnType" : {
"string" : {

}
}
},
{
"abiName" : "bjs_roundTripOptionalComplexResult",
"effects" : {
Expand Down
9 changes: 9 additions & 0 deletions Tests/prelude.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,15 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) {
assert.deepEqual(exports.roundTripOptionalAPIResult(p1), p1);
assert.deepEqual(exports.roundTripOptionalComplexResult(cl1), cl1);

const apiSuccess = { tag: exports.APIResult.Tag.Success, param0: "test success" };
const apiFailure = { tag: exports.APIResult.Tag.Failure, param0: 404 };
const apiInfo = { tag: exports.APIResult.Tag.Info };

assert.equal(exports.compareAPIResults(apiSuccess, apiFailure), "r1:success:test success,r2:failure:404");
assert.equal(exports.compareAPIResults(null, apiInfo), "r1:nil,r2:info");
assert.equal(exports.compareAPIResults(apiFailure, null), "r1:failure:404,r2:nil");
assert.equal(exports.compareAPIResults(null, null), "r1:nil,r2:nil");

const optionalGreeter = new exports.Greeter("Schrödinger");
const optionalGreeter2 = exports.roundTripOptionalClass(optionalGreeter);
assert.equal(optionalGreeter2?.greet() ?? "", "Hello, Schrödinger!");
Expand Down