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
10 changes: 9 additions & 1 deletion Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,10 @@ extension BridgeType {
}
case .namespaceEnum:
throw BridgeJSCoreError("Namespace enums cannot be used as parameters")
case .nullable(.swiftStruct, _) where context == .importTS:
// Optional `@JS struct`s bridge through the stack (isSome discriminator + fields),
// like optional arrays/dictionaries, rather than the non-optional object-id ABI.
return LoweringParameterInfo(loweredParameters: [("isSome", .i32)])
case .nullable(let wrappedType, _):
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
var params = [("isSome", WasmCoreType.i32)]
Expand Down Expand Up @@ -1034,10 +1038,14 @@ extension BridgeType {
case .namespaceEnum:
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
case .nullable(let wrappedType, _):
// jsObject uses stack ABI for optionals — returns void, value goes through stacks
// jsObject and `@JS struct` use the stack ABI for optionals — the thunk returns
// void and the value (plus isSome discriminator) flows through the stacks.
if case .jsObject = wrappedType {
return LiftingReturnInfo(valueToLift: nil)
}
if case .swiftStruct = wrappedType, context == .importTS {
return LiftingReturnInfo(valueToLift: nil)
}
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
case .array, .dictionary:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ struct Point {
}

@JSFunction func translate(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point

@JSFunction func roundTripOptional(_ point: Point?) throws(JSException) -> Point?
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,40 @@
"_0" : "Point"
}
}
},
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
"isThrows" : true
},
"name" : "roundTripOptional",
"parameters" : [
{
"name" : "point",
"type" : {
"nullable" : {
"_0" : {
"swiftStruct" : {
"_0" : "Point"
}
},
"_1" : "null"
}
}
}
],
"returnType" : {
"nullable" : {
"_0" : {
"swiftStruct" : {
"_0" : "Point"
}
},
"_1" : "null"
}
}
}
],
"types" : [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,25 @@ func _$translate(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException) -> Po
throw error
}
return Point.bridgeJSLiftReturn(ret)
}

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_roundTripOptional")
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void
#else
fileprivate func bjs_roundTripOptional_extern(_ point: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_roundTripOptional(_ point: Int32) -> Void {
return bjs_roundTripOptional_extern(point)
}

func _$roundTripOptional(_ point: Optional<Point>) throws(JSException) -> Optional<Point> {
let pointIsSome = point.bridgeJSLowerParameter()
bjs_roundTripOptional(pointIsSome)
if let error = _swift_js_take_exception() {
throw error
}
return Optional<Point>.bridgeJSLiftReturn()
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type Exports = {
}
export type Imports = {
translate(point: Point, dx: number, dy: number): Point;
roundTripOptional(point: Point | null): Point | null;
}
export function createInstantiator(options: {
imports: Imports;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,25 @@ export async function createInstantiator(options, swift) {
setException(error);
}
}
TestModule["bjs_roundTripOptional"] = function bjs_roundTripOptional(point) {
try {
let optResult;
if (point) {
const struct = structHelpers.Point.lift();
optResult = struct;
} else {
optResult = null;
}
let ret = imports.roundTripOptional(optResult);
const isSome = ret != null;
if (isSome) {
structHelpers.Point.lower(ret);
}
i32Stack.push(isSome ? 1 : 0);
} catch (error) {
setException(error);
}
}
},
setInstance: (i) => {
instance = i;
Expand Down
21 changes: 21 additions & 0 deletions Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13279,6 +13279,27 @@ func _$jsTranslatePoint(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException
return Point.bridgeJSLiftReturn(ret)
}

#if arch(wasm32)
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_jsRoundTripOptionalPoint")
fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void
#else
fileprivate func bjs_jsRoundTripOptionalPoint_extern(_ point: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_jsRoundTripOptionalPoint(_ point: Int32) -> Void {
return bjs_jsRoundTripOptionalPoint_extern(point)
}

func _$jsRoundTripOptionalPoint(_ point: Optional<Point>) throws(JSException) -> Optional<Point> {
let pointIsSome = point.bridgeJSLowerParameter()
bjs_jsRoundTripOptionalPoint(pointIsSome)
if let error = _swift_js_take_exception() {
throw error
}
return Optional<Point>.bridgeJSLiftReturn()
}

#if arch(wasm32)
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_IntegerTypesSupportImports_jsRoundTripInt_static")
fileprivate func bjs_IntegerTypesSupportImports_jsRoundTripInt_static_extern(_ v: Int32) -> Int32
Expand Down
34 changes: 34 additions & 0 deletions Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json
Original file line number Diff line number Diff line change
Expand Up @@ -19745,6 +19745,40 @@
"_0" : "Point"
}
}
},
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
"isThrows" : true
},
"name" : "jsRoundTripOptionalPoint",
"parameters" : [
{
"name" : "point",
"type" : {
"nullable" : {
"_0" : {
"swiftStruct" : {
"_0" : "Point"
}
},
"_1" : "null"
}
}
}
],
"returnType" : {
"nullable" : {
"_0" : {
"swiftStruct" : {
"_0" : "Point"
}
},
"_1" : "null"
}
}
}
],
"types" : [
Expand Down
7 changes: 7 additions & 0 deletions Tests/BridgeJSRuntimeTests/ImportAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ class ImportAPITests: XCTestCase {
}
}

func testRoundTripOptionalStruct() throws {
let p = try jsRoundTripOptionalPoint(Point(x: 3, y: 4))
XCTAssertEqual(p?.x, 3)
XCTAssertEqual(p?.y, 4)
XCTAssertNil(try jsRoundTripOptionalPoint(nil))
}

func ensureThrows<T>(_ f: (Bool) throws(JSException) -> T) throws {
do {
_ = try f(true)
Expand Down
2 changes: 2 additions & 0 deletions Tests/BridgeJSRuntimeTests/ImportStructAPIs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ struct Point {
}

@JSFunction func jsTranslatePoint(_ point: Point, dx: Int, dy: Int) throws(JSException) -> Point

@JSFunction func jsRoundTripOptionalPoint(_ point: Point?) throws(JSException) -> Point?
1 change: 1 addition & 0 deletions Tests/prelude.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export async function setupOptions(options, context) {
jsTranslatePoint: (point, dx, dy) => {
return { x: (point.x | 0) + (dx | 0), y: (point.y | 0) + (dy | 0) };
},
jsRoundTripOptionalPoint: (point) => point,
roundTripArrayMembers: (value) => {
return value;
},
Expand Down
Loading