Skip to content

Commit c8de4e0

Browse files
BridgeJS: Stop spreading isAsync handling outside of CallJSEmission
1 parent b472133 commit c8de4e0

File tree

3 files changed

+48
-59
lines changed

3 files changed

+48
-59
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public struct ClosureCodegen {
3232
let builder = try ImportTS.CallJSEmission(
3333
moduleName: "bjs",
3434
abiName: externName,
35+
effects: Effects(isAsync: signature.isAsync, isThrows: signature.isThrows),
3536
returnType: signature.returnType,
3637
context: .exportSwift
3738
)

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,7 @@ struct ProtocolCodegen {
12281228
let builder = try ImportTS.CallJSEmission(
12291229
moduleName: moduleName,
12301230
abiName: "_extern_\(method.name)",
1231+
effects: method.effects,
12311232
returnType: method.returnType,
12321233
context: .exportSwift
12331234
)
@@ -1327,6 +1328,7 @@ struct ProtocolCodegen {
13271328
let getterBuilder = try ImportTS.CallJSEmission(
13281329
moduleName: moduleName,
13291330
abiName: getterAbiName,
1331+
effects: Effects(isAsync: false, isThrows: false),
13301332
returnType: property.type,
13311333
context: .exportSwift
13321334
)
@@ -1360,6 +1362,7 @@ struct ProtocolCodegen {
13601362
let setterBuilder = try ImportTS.CallJSEmission(
13611363
moduleName: moduleName,
13621364
abiName: setterAbiName,
1365+
effects: Effects(isAsync: false, isThrows: false),
13631366
returnType: .void,
13641367
context: .exportSwift
13651368
)

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public struct ImportTS {
6969
let builder = try CallJSEmission(
7070
moduleName: moduleName,
7171
abiName: getter.abiName(context: nil),
72+
effects: Effects(isAsync: false, isThrows: true),
7273
returnType: getter.type
7374
)
7475
try builder.call()
@@ -94,13 +95,14 @@ public struct ImportTS {
9495

9596
let abiName: String
9697
let moduleName: String
98+
let effects: Effects
9799
let returnType: BridgeType
98100
let context: BridgeContext
99101

100102
var body = CodeFragmentPrinter()
101103
var abiParameterForwardings: [String] = []
102104
var abiParameterSignatures: [(name: String, type: WasmCoreType)] = []
103-
var abiReturnType: WasmCoreType?
105+
let abiReturnType: WasmCoreType?
104106
// Track destructured variable names for multiple lowered parameters
105107
var destructuredVarNames: [String] = []
106108
// Stack-lowered parameters should be evaluated in reverse order to match LIFO stacks
@@ -111,15 +113,23 @@ public struct ImportTS {
111113
private var borrowedArguments: [BorrowedArgument] = []
112114
private let needsReturnVariable: Bool
113115

114-
init(moduleName: String, abiName: String, returnType: BridgeType, context: BridgeContext = .importTS) throws {
116+
init(moduleName: String, abiName: String, effects: Effects, returnType: BridgeType, context: BridgeContext = .importTS) throws {
115117
self.moduleName = moduleName
116118
self.abiName = abiName
119+
self.effects = effects
117120
self.returnType = returnType
118121
self.context = context
119122
let liftingInfo = try returnType.liftingReturnInfo(context: context)
120-
needsReturnVariable =
121-
!(returnType == .void || returnType.usesSideChannelForOptionalReturn()
122-
|| liftingInfo.valueToLift == nil)
123+
if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn() {
124+
abiReturnType = nil
125+
} else {
126+
abiReturnType = liftingInfo.valueToLift
127+
}
128+
needsReturnVariable = abiReturnType != nil
129+
130+
if effects.isAsync {
131+
prependClosureCallbackParams()
132+
}
123133
}
124134

125135
func lowerParameter(param: Parameter) throws {
@@ -216,12 +226,12 @@ public struct ImportTS {
216226
///
217227
/// Used for async imports where the JS side receives closure-backed
218228
/// resolve/reject callbacks as object references.
219-
func prependClosureCallbackParams() {
229+
private func prependClosureCallbackParams() {
220230
abiParameterSignatures.insert(contentsOf: [("resolveRef", .i32), ("rejectRef", .i32)], at: 0)
221231
abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0)
222232
}
223233

224-
func call(skipExceptionCheck: Bool = false) throws {
234+
func call() throws {
225235
for stmt in stackLoweringStmts {
226236
body.write(stmt.description)
227237
}
@@ -254,25 +264,30 @@ public struct ImportTS {
254264

255265
// Add exception check for ImportTS context (skipped for async, where
256266
// errors are funneled through the JS-side reject path)
257-
if !skipExceptionCheck && context == .importTS {
267+
if !effects.isAsync && context == .importTS {
258268
body.write("if let error = _swift_js_take_exception() { throw error }")
259269
}
260270
}
261271

262272
func liftReturnValue() throws {
273+
if effects.isAsync {
274+
liftAsyncReturnValue()
275+
} else {
276+
try liftSyncReturnValue()
277+
}
278+
}
279+
280+
private func liftSyncReturnValue() throws {
263281
let liftingInfo = try returnType.liftingReturnInfo(context: context)
264282

265283
if returnType == .void {
266-
abiReturnType = nil
267284
return
268285
}
269286

270287
if returnType.usesSideChannelForOptionalReturn() {
271288
// Side channel returns: extern function returns Void, value is retrieved via side channel
272-
abiReturnType = nil
273289
body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()")
274290
} else {
275-
abiReturnType = liftingInfo.valueToLift
276291
let liftExpr: String
277292
switch returnType {
278293
case .closure(let signature, _):
@@ -288,25 +303,24 @@ public struct ImportTS {
288303
}
289304
}
290305

291-
func liftAsyncReturnValue(originalReturnType: BridgeType) {
306+
private func liftAsyncReturnValue() {
292307
// For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32`
293308
// and returns void. The JS side calls the resolve/reject closures when the Promise settles.
294309
// The resolve closure is typed to match the return type, so the ABI conversion is handled
295310
// by the existing closure codegen infrastructure — no manual JSValue-to-type switch needed.
296-
abiReturnType = nil
297311

298312
// Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise
299313
let innerBody = body
300314
body = CodeFragmentPrinter()
301315

302316
let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }"
303-
if originalReturnType == .void {
317+
if returnType == .void {
304318
let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }"
305319
body.write(
306320
"try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
307321
)
308322
} else {
309-
let resolveSwiftType = originalReturnType.closureSwiftType
323+
let resolveSwiftType = returnType.closureSwiftType
310324
let resolveFactory =
311325
"makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }"
312326
body.write(
@@ -318,19 +332,11 @@ public struct ImportTS {
318332
}
319333
body.write("}")
320334

321-
if originalReturnType != .void {
335+
if returnType != .void {
322336
body.write("return resolved")
323337
}
324338
}
325339

326-
func assignThis(returnType: BridgeType) {
327-
guard case .jsObject = returnType else {
328-
preconditionFailure("assignThis can only be called with a jsObject return type")
329-
}
330-
abiReturnType = .i32
331-
body.write("self.jsObject = JSObject(id: UInt32(bitPattern: ret))")
332-
}
333-
334340
func renderImportDecl() -> DeclSyntax {
335341
let printer = CodeFragmentPrinter()
336342
SwiftCodePattern.buildExternFunctionDecl(
@@ -408,26 +414,17 @@ public struct ImportTS {
408414
_ function: ImportedFunctionSkeleton,
409415
topLevelDecls: inout [DeclSyntax]
410416
) throws -> [DeclSyntax] {
411-
// For async functions, the extern returns void (the JS side resolves/rejects
412-
// via continuation callbacks). For sync functions, use the actual return type.
413-
let abiReturnType: BridgeType = function.effects.isAsync ? .void : function.returnType
414417
let builder = try CallJSEmission(
415418
moduleName: moduleName,
416419
abiName: function.abiName(context: nil),
417-
returnType: abiReturnType
420+
effects: function.effects,
421+
returnType: function.returnType
418422
)
419-
if function.effects.isAsync {
420-
builder.prependClosureCallbackParams()
421-
}
422423
for param in function.parameters {
423424
try builder.lowerParameter(param: param)
424425
}
425-
try builder.call(skipExceptionCheck: function.effects.isAsync)
426-
if function.effects.isAsync {
427-
builder.liftAsyncReturnValue(originalReturnType: function.returnType)
428-
} else {
429-
try builder.liftReturnValue()
430-
}
426+
try builder.call()
427+
try builder.liftReturnValue()
431428
topLevelDecls.append(builder.renderImportDecl())
432429
return [
433430
builder.renderThunkDecl(
@@ -445,25 +442,18 @@ public struct ImportTS {
445442
var decls: [DeclSyntax] = []
446443

447444
func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] {
448-
let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType
449445
let builder = try CallJSEmission(
450446
moduleName: moduleName,
451447
abiName: method.abiName(context: type),
452-
returnType: abiReturnType
448+
effects: method.effects,
449+
returnType: method.returnType
453450
)
454-
if method.effects.isAsync {
455-
builder.prependClosureCallbackParams()
456-
}
457451
try builder.lowerParameter(param: selfParameter)
458452
for param in method.parameters {
459453
try builder.lowerParameter(param: param)
460454
}
461-
try builder.call(skipExceptionCheck: method.effects.isAsync)
462-
if method.effects.isAsync {
463-
builder.liftAsyncReturnValue(originalReturnType: method.returnType)
464-
} else {
465-
try builder.liftReturnValue()
466-
}
455+
try builder.call()
456+
try builder.liftReturnValue()
467457
topLevelDecls.append(builder.renderImportDecl())
468458
return [
469459
builder.renderThunkDecl(
@@ -477,20 +467,12 @@ public struct ImportTS {
477467

478468
func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] {
479469
let abiName = method.abiName(context: type, operation: "static")
480-
let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType
481-
let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, returnType: abiReturnType)
482-
if method.effects.isAsync {
483-
builder.prependClosureCallbackParams()
484-
}
470+
let builder = try CallJSEmission(moduleName: moduleName, abiName: abiName, effects: method.effects, returnType: method.returnType)
485471
for param in method.parameters {
486472
try builder.lowerParameter(param: param)
487473
}
488-
try builder.call(skipExceptionCheck: method.effects.isAsync)
489-
if method.effects.isAsync {
490-
builder.liftAsyncReturnValue(originalReturnType: method.returnType)
491-
} else {
492-
try builder.liftReturnValue()
493-
}
474+
try builder.call()
475+
try builder.liftReturnValue()
494476
topLevelDecls.append(builder.renderImportDecl())
495477
return [
496478
builder.renderThunkDecl(
@@ -506,6 +488,7 @@ public struct ImportTS {
506488
let builder = try CallJSEmission(
507489
moduleName: moduleName,
508490
abiName: constructor.abiName(context: type),
491+
effects: Effects(isAsync: false, isThrows: true),
509492
returnType: .jsObject(nil)
510493
)
511494
for param in constructor.parameters {
@@ -527,6 +510,7 @@ public struct ImportTS {
527510
let builder = try CallJSEmission(
528511
moduleName: moduleName,
529512
abiName: getter.abiName(context: type),
513+
effects: Effects(isAsync: false, isThrows: true),
530514
returnType: getter.type
531515
)
532516
try builder.lowerParameter(param: selfParameter)
@@ -546,6 +530,7 @@ public struct ImportTS {
546530
let builder = try CallJSEmission(
547531
moduleName: moduleName,
548532
abiName: setter.abiName(context: type),
533+
effects: Effects(isAsync: false, isThrows: true),
549534
returnType: .void
550535
)
551536
let newValue = Parameter(label: nil, name: "newValue", type: setter.type)

0 commit comments

Comments
 (0)