Skip to content

Commit 2388473

Browse files
authored
Merge pull request #755 from PassiveLogic/kr/nullable-struct-import
BridgeJS: Support optional @js struct in imported function signatures
2 parents d1e0f95 + 2941cd2 commit 2388473

11 files changed

Lines changed: 151 additions & 1 deletion

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,10 @@ extension BridgeType {
957957
}
958958
case .namespaceEnum:
959959
throw BridgeJSCoreError("Namespace enums cannot be used as parameters")
960+
case .nullable(.swiftStruct, _) where context == .importTS:
961+
// Optional `@JS struct`s bridge through the stack (isSome discriminator + fields),
962+
// like optional arrays/dictionaries, rather than the non-optional object-id ABI.
963+
return LoweringParameterInfo(loweredParameters: [("isSome", .i32)])
960964
case .nullable(let wrappedType, _):
961965
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
962966
var params = [("isSome", WasmCoreType.i32)]
@@ -1034,10 +1038,14 @@ extension BridgeType {
10341038
case .namespaceEnum:
10351039
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
10361040
case .nullable(let wrappedType, _):
1037-
// jsObject uses stack ABI for optionals — returns void, value goes through stacks
1041+
// jsObject and `@JS struct` use the stack ABI for optionals — the thunk returns
1042+
// void and the value (plus isSome discriminator) flows through the stacks.
10381043
if case .jsObject = wrappedType {
10391044
return LiftingReturnInfo(valueToLift: nil)
10401045
}
1046+
if case .swiftStruct = wrappedType, context == .importTS {
1047+
return LiftingReturnInfo(valueToLift: nil)
1048+
}
10411049
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
10421050
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10431051
case .array, .dictionary:

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ struct Point {
55
}
66

77
@JSFunction func translate(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point
8+
9+
@JSFunction func roundTripOptional(_ point: Point?) throws(JSException) -> Point?

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,40 @@
100100
"_0" : "Point"
101101
}
102102
}
103+
},
104+
{
105+
"accessLevel" : "internal",
106+
"effects" : {
107+
"isAsync" : false,
108+
"isStatic" : false,
109+
"isThrows" : true
110+
},
111+
"name" : "roundTripOptional",
112+
"parameters" : [
113+
{
114+
"name" : "point",
115+
"type" : {
116+
"nullable" : {
117+
"_0" : {
118+
"swiftStruct" : {
119+
"_0" : "Point"
120+
}
121+
},
122+
"_1" : "null"
123+
}
124+
}
125+
}
126+
],
127+
"returnType" : {
128+
"nullable" : {
129+
"_0" : {
130+
"swiftStruct" : {
131+
"_0" : "Point"
132+
}
133+
},
134+
"_1" : "null"
135+
}
136+
}
103137
}
104138
],
105139
"types" : [

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,25 @@ func _$translate(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException) -> Po
6767
throw error
6868
}
6969
return Point.bridgeJSLiftReturn(ret)
70+
}
71+
72+
#if arch(wasm32)
73+
@_extern(wasm, module: "TestModule", name: "bjs_roundTripOptional")
74+
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void
75+
#else
76+
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void {
77+
fatalError("Only available on WebAssembly")
78+
}
79+
#endif
80+
@inline(never) fileprivate func bjs_roundTripOptional(_ point: Int32) -> Void {
81+
return bjs_roundTripOptional_extern(point)
82+
}
83+
84+
func _$roundTripOptional(_ point: Optional<Point>) throws(JSException) -> Optional<Point> {
85+
let pointIsSome = point.bridgeJSLowerParameter()
86+
bjs_roundTripOptional(pointIsSome)
87+
if let error = _swift_js_take_exception() {
88+
throw error
89+
}
90+
return Optional<Point>.bridgeJSLiftReturn()
7091
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type Exports = {
1212
}
1313
export type Imports = {
1414
translate(point: Point, dx: number, dy: number): Point;
15+
roundTripOptional(point: Point | null): Point | null;
1516
}
1617
export function createInstantiator(options: {
1718
imports: Imports;

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,25 @@ export async function createInstantiator(options, swift) {
226226
setException(error);
227227
}
228228
}
229+
TestModule["bjs_roundTripOptional"] = function bjs_roundTripOptional(point) {
230+
try {
231+
let optResult;
232+
if (point) {
233+
const struct = structHelpers.Point.lift();
234+
optResult = struct;
235+
} else {
236+
optResult = null;
237+
}
238+
let ret = imports.roundTripOptional(optResult);
239+
const isSome = ret != null;
240+
if (isSome) {
241+
structHelpers.Point.lower(ret);
242+
}
243+
i32Stack.push(isSome ? 1 : 0);
244+
} catch (error) {
245+
setException(error);
246+
}
247+
}
229248
},
230249
setInstance: (i) => {
231250
instance = i;

Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13279,6 +13279,27 @@ func _$jsTranslatePoint(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException
1327913279
return Point.bridgeJSLiftReturn(ret)
1328013280
}
1328113281

13282+
#if arch(wasm32)
13283+
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalPoint")
13284+
fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void
13285+
#else
13286+
fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void {
13287+
fatalError("Only available on WebAssembly")
13288+
}
13289+
#endif
13290+
@inline(never) fileprivate func bjs_jsRoundTripOptionalPoint(_ point: Int32) -> Void {
13291+
return bjs_jsRoundTripOptionalPoint_extern(point)
13292+
}
13293+
13294+
func _$jsRoundTripOptionalPoint(_ point: Optional<Point>) throws(JSException) -> Optional<Point> {
13295+
let pointIsSome = point.bridgeJSLowerParameter()
13296+
bjs_jsRoundTripOptionalPoint(pointIsSome)
13297+
if let error = _swift_js_take_exception() {
13298+
throw error
13299+
}
13300+
return Optional<Point>.bridgeJSLiftReturn()
13301+
}
13302+
1328213303
#if arch(wasm32)
1328313304
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_IntegerTypesSupportImports_jsRoundTripInt_static")
1328413305
fileprivate func bjs_IntegerTypesSupportImports_jsRoundTripInt_static_extern(_ v: Int32) -> Int32

Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19745,6 +19745,40 @@
1974519745
"_0" : "Point"
1974619746
}
1974719747
}
19748+
},
19749+
{
19750+
"accessLevel" : "internal",
19751+
"effects" : {
19752+
"isAsync" : false,
19753+
"isStatic" : false,
19754+
"isThrows" : true
19755+
},
19756+
"name" : "jsRoundTripOptionalPoint",
19757+
"parameters" : [
19758+
{
19759+
"name" : "point",
19760+
"type" : {
19761+
"nullable" : {
19762+
"_0" : {
19763+
"swiftStruct" : {
19764+
"_0" : "Point"
19765+
}
19766+
},
19767+
"_1" : "null"
19768+
}
19769+
}
19770+
}
19771+
],
19772+
"returnType" : {
19773+
"nullable" : {
19774+
"_0" : {
19775+
"swiftStruct" : {
19776+
"_0" : "Point"
19777+
}
19778+
},
19779+
"_1" : "null"
19780+
}
19781+
}
1974819782
}
1974919783
],
1975019784
"types" : [

Tests/BridgeJSRuntimeTests/ImportAPITests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ class ImportAPITests: XCTestCase {
5959
}
6060
}
6161

62+
func testRoundTripOptionalStruct() throws {
63+
let p = try jsRoundTripOptionalPoint(Point(x: 3, y: 4))
64+
XCTAssertEqual(p?.x, 3)
65+
XCTAssertEqual(p?.y, 4)
66+
XCTAssertNil(try jsRoundTripOptionalPoint(nil))
67+
}
68+
6269
func ensureThrows<T>(_ f: (Bool) throws(JSException) -> T) throws {
6370
do {
6471
_ = try f(true)

Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ struct Point {
77
}
88

99
@JSFunction func jsTranslatePoint(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point
10+
11+
@JSFunction func jsRoundTripOptionalPoint(_ point: Point?) throws(JSException) -> Point?

0 commit comments

Comments
 (0)