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
29 changes: 6 additions & 23 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -933,19 +933,10 @@ extension BridgeType {
case .associatedValueEnum:
return LoweringParameterInfo(loweredParameters: [("caseId", .i32)])
case .swiftStruct:
switch context {
case .importTS:
// Swift structs are bridged as JS objects (object IDs) in imported signatures.
return LoweringParameterInfo(loweredParameters: [("objectId", .i32)])
case .exportSwift:
return LoweringParameterInfo(loweredParameters: [])
}
// `@JS struct` parameters always use the stack ABI (same as arrays/dictionaries).
return LoweringParameterInfo(loweredParameters: [])
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 DownExpand Up@@ -1005,24 +996,16 @@ extension BridgeType {
case .associatedValueEnum:
return LiftingReturnInfo(valueToLift: .i32)
case .swiftStruct:
switch context {
case .importTS:
// Swift structs are bridged as JS objects (object IDs) in imported signatures.
return LiftingReturnInfo(valueToLift: .i32)
case .exportSwift:
return LiftingReturnInfo(valueToLift: nil)
}
// `@JS struct` returns always use the stack ABI (same as arrays/dictionaries).
return LiftingReturnInfo(valueToLift: nil)
case .namespaceEnum:
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
case .nullable(let wrappedType, _):
// jsObject and `@JS struct` use the stack ABI for optionals — the thunk returns
// void and the value (plus isSome discriminator) flows through the stacks.
// jsObject uses 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
36 changes: 13 additions & 23 deletions Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -1437,23 +1437,18 @@ struct IntrinsicJSFragment: Sendable {
}
)
case .swiftStruct(let fullName):
switch context {
case .importTS:
return .jsObjectLiftRetainedObjectId
case .exportSwift:
let base = fullName.replacingOccurrences(of: ".", with: "_")
return IntrinsicJSFragment(
parameters: [],
printCode: { arguments, context in
let (scope, printer) = (context.scope, context.printer)
let resultVar = scope.variable("structValue")
printer.write(
"const \(resultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(base).lift();"
)
return [resultVar]
}
)
}
let base = fullName.replacingOccurrences(of: ".", with: "_")
return IntrinsicJSFragment(
parameters: [],
printCode: { arguments, context in
let (scope, printer) = (context.scope, context.printer)
let resultVar = scope.variable("structValue")
printer.write(
"const \(resultVar) = \(JSGlueVariableScope.reservedStructHelpers).\(base).lift();"
)
return [resultVar]
}
)
case .closure:
return IntrinsicJSFragment(
parameters: ["funcRef"],
Expand DownExpand Up@@ -1497,12 +1492,7 @@ struct IntrinsicJSFragment: Sendable {
case .associatedValueEnum(let fullName):
return associatedValueLowerReturn(fullName: fullName)
case .swiftStruct(let fullName):
switch context {
case .importTS:
return .jsObjectLowerReturn
case .exportSwift:
return swiftStructLowerReturn(fullName: fullName)
}
return swiftStructLowerReturn(fullName: fullName)
case .closure:
return IntrinsicJSFragment(
parameters: ["value"],
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,20 +507,20 @@ func _$Promise_resolve_8JSObjectC(_ promise: JSObject, _ value: JSObject) throws

#if arch(wasm32)
@_extern(wasm, module: "bjs", name: "promise_resolve_TestModule_10AsyncPointV")
fileprivate func promise_resolve_TestModule_10AsyncPointV_extern(_ promise: Int32, _ value: Int32) -> Void
fileprivate func promise_resolve_TestModule_10AsyncPointV_extern(_ promise: Int32) -> Void
#else
fileprivate func promise_resolve_TestModule_10AsyncPointV_extern(_ promise: Int32, _ value: Int32) -> Void {
fileprivate func promise_resolve_TestModule_10AsyncPointV_extern(_ promise: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func promise_resolve_TestModule_10AsyncPointV(_ promise: Int32, _ value: Int32) -> Void {
return promise_resolve_TestModule_10AsyncPointV_extern(promise, value)
@inline(never) fileprivate func promise_resolve_TestModule_10AsyncPointV(_ promise: Int32) -> Void {
return promise_resolve_TestModule_10AsyncPointV_extern(promise)
}

func _$Promise_resolve_10AsyncPointV(_ promise: JSObject, _ value: AsyncPoint) throws(JSException) -> Void {
let valueObjectId = value.bridgeJSLowerParameter()
let _ = value.bridgeJSLowerParameter()
let promiseValue = promise.bridgeJSLowerParameter()
promise_resolve_TestModule_10AsyncPointV(promiseValue, valueObjectId)
promise_resolve_TestModule_10AsyncPointV(promiseValue)
if let error = _swift_js_take_exception() { throw error }
}

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2684,20 +2684,20 @@ func _$Promise_resolve_SS(_ promise: JSObject, _ value: String) throws(JSExcepti

#if arch(wasm32)
@_extern(wasm, module: "bjs", name: "promise_resolve_TestModule_6AnimalV")
fileprivate func promise_resolve_TestModule_6AnimalV_extern(_ promise: Int32, _ value: Int32) -> Void
fileprivate func promise_resolve_TestModule_6AnimalV_extern(_ promise: Int32) -> Void
#else
fileprivate func promise_resolve_TestModule_6AnimalV_extern(_ promise: Int32, _ value: Int32) -> Void {
fileprivate func promise_resolve_TestModule_6AnimalV_extern(_ promise: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func promise_resolve_TestModule_6AnimalV(_ promise: Int32, _ value: Int32) -> Void {
return promise_resolve_TestModule_6AnimalV_extern(promise, value)
@inline(never) fileprivate func promise_resolve_TestModule_6AnimalV(_ promise: Int32) -> Void {
return promise_resolve_TestModule_6AnimalV_extern(promise)
}

func _$Promise_resolve_6AnimalV(_ promise: JSObject, _ value: Animal) throws(JSException) -> Void {
let valueObjectId = value.bridgeJSLowerParameter()
let _ = value.bridgeJSLowerParameter()
let promiseValue = promise.bridgeJSLowerParameter()
promise_resolve_TestModule_6AnimalV(promiseValue, valueObjectId)
promise_resolve_TestModule_6AnimalV(promiseValue)
if let error = _swift_js_take_exception() { throw error }
}

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,25 +48,25 @@ fileprivate func _bjs_struct_lift_Point_extern() -> Int32 {

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

func _$translate(_ point: Point, _ dx: Int, _ dy: Int) throws(JSException) -> Point {
let dyValue = dy.bridgeJSLowerParameter()
let dxValue = dx.bridgeJSLowerParameter()
let pointObjectId = point.bridgeJSLowerParameter()
let ret = bjs_translate(pointObjectId, dxValue, dyValue)
let _ = point.bridgeJSLowerParameter()
bjs_translate(dxValue, dyValue)
if let error = _swift_js_take_exception() {
throw error
}
return Point.bridgeJSLiftReturn(ret)
return Point.bridgeJSLiftReturn()
}

#if arch(wasm32)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -280,11 +280,10 @@ export async function createInstantiator(options, swift) {
setException(error);
}
}
bjs["promise_resolve_TestModule_10AsyncPointV"] = function(promise, value) {
bjs["promise_resolve_TestModule_10AsyncPointV"] = function(promise) {
try {
const value1 = swift.memory.getObject(value);
swift.memory.release(value);
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(value1);
const structValue = structHelpers.AsyncPoint.lift();
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(structValue);
} catch (error) {
setException(error);
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -345,11 +345,10 @@ export async function createInstantiator(options, swift) {
setException(error);
}
}
bjs["promise_resolve_TestModule_6AnimalV"] = function(promise, value) {
bjs["promise_resolve_TestModule_6AnimalV"] = function(promise) {
try {
const value1 = swift.memory.getObject(value);
swift.memory.release(value);
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(value1);
const structValue = structHelpers.Animal.lift();
swift.memory.getObject(promise)[__bjs_promiseSettlers].resolve(structValue);
} catch (error) {
setException(error);
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -223,12 +223,11 @@ export async function createInstantiator(options, swift) {
}
bjs["swift_js_closure_unregister"] = function(funcRef) {}
const TestModule = importObject["TestModule"] = importObject["TestModule"] || {};
TestModule["bjs_translate"] = function bjs_translate(point, dx, dy) {
TestModule["bjs_translate"] = function bjs_translate(dx, dy) {
try {
const value = swift.memory.getObject(point);
swift.memory.release(point);
let ret = imports.translate(value, dx, dy);
return swift.memory.retain(ret);
const structValue = structHelpers.Point.lift();
let ret = imports.translate(structValue, dx, dy);
structHelpers.Point.lower(ret);
} catch (error) {
setException(error);
}
Expand Down
13 changes: 6 additions & 7 deletions Sources/JavaScriptKit/BridgeJSIntrinsics.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -1133,13 +1133,12 @@ where StackLiftResult == Self {
}

extension _BridgedSwiftStruct {
@_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> Int32 {
return toJSObject().bridgeJSLowerReturn()
}

@_spi(BridgeJS) public static func bridgeJSLiftReturn(_ objectId: Int32) -> Self {
let jsObject = JSObject.bridgeJSLiftReturn(objectId)
return Self(unsafelyCopying: jsObject)
/// Lower a struct parameter onto the shared stacks for the peer to `lift()`.
///
/// Same convention as arrays/dictionaries. Use ``toJSObject()`` when a real JS object
/// representation is needed (e.g. `init(unsafelyCopying:)` round-trips).
@_spi(BridgeJS) public consuming func bridgeJSLowerParameter() {
bridgeJSStackPush()
}

@_spi(BridgeJS) public static func bridgeJSLiftReturn() -> Self {
Expand Down
9 changes: 9 additions & 0 deletions Tests/BridgeJSRuntimeTests/ExportAPITests.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -1339,6 +1339,15 @@ enum GraphOperations {
let noneStr = none.map { "(\($0.dx),\($0.dy))" } ?? "nil"
return "\(someStr) | \(noneStr)"
}

/// Swift→JS callback with a struct parameter (ExportSwift stack ABI).
@JS func observeVector(_ callback: (Vector2D) -> Void) {
callback(Vector2D(dx: 1.5, dy: 2.5))
}

@JS func mapVector(_ vector: Vector2D, _ callback: (Vector2D) -> Vector2D) -> Vector2D {
return callback(vector)
}
}

@JS enum NestedStructGroupA {
Expand Down
Loading
Loading