Skip to content

Commit 14561d4

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

File tree

3 files changed

+59
-59
lines changed

3 files changed

+59
-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: 55 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,29 @@ 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(
117+
moduleName: String,
118+
abiName: String,
119+
effects: Effects,
120+
returnType: BridgeType,
121+
context: BridgeContext = .importTS
122+
) throws {
115123
self.moduleName = moduleName
116124
self.abiName = abiName
125+
self.effects = effects
117126
self.returnType = returnType
118127
self.context = context
119128
let liftingInfo = try returnType.liftingReturnInfo(context: context)
120-
needsReturnVariable =
121-
!(returnType == .void || returnType.usesSideChannelForOptionalReturn()
122-
|| liftingInfo.valueToLift == nil)
129+
if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn() {
130+
abiReturnType = nil
131+
} else {
132+
abiReturnType = liftingInfo.valueToLift
133+
}
134+
needsReturnVariable = abiReturnType != nil
135+
136+
if effects.isAsync {
137+
prependClosureCallbackParams()
138+
}
123139
}
124140

125141
func lowerParameter(param: Parameter) throws {
@@ -216,12 +232,12 @@ public struct ImportTS {
216232
///
217233
/// Used for async imports where the JS side receives closure-backed
218234
/// resolve/reject callbacks as object references.
219-
func prependClosureCallbackParams() {
235+
private func prependClosureCallbackParams() {
220236
abiParameterSignatures.insert(contentsOf: [("resolveRef", .i32), ("rejectRef", .i32)], at: 0)
221237
abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0)
222238
}
223239

224-
func call(skipExceptionCheck: Bool = false) throws {
240+
func call() throws {
225241
for stmt in stackLoweringStmts {
226242
body.write(stmt.description)
227243
}
@@ -254,25 +270,30 @@ public struct ImportTS {
254270

255271
// Add exception check for ImportTS context (skipped for async, where
256272
// errors are funneled through the JS-side reject path)
257-
if !skipExceptionCheck && context == .importTS {
273+
if !effects.isAsync && context == .importTS {
258274
body.write("if let error = _swift_js_take_exception() { throw error }")
259275
}
260276
}
261277

262278
func liftReturnValue() throws {
279+
if effects.isAsync {
280+
liftAsyncReturnValue()
281+
} else {
282+
try liftSyncReturnValue()
283+
}
284+
}
285+
286+
private func liftSyncReturnValue() throws {
263287
let liftingInfo = try returnType.liftingReturnInfo(context: context)
264288

265289
if returnType == .void {
266-
abiReturnType = nil
267290
return
268291
}
269292

270293
if returnType.usesSideChannelForOptionalReturn() {
271294
// Side channel returns: extern function returns Void, value is retrieved via side channel
272-
abiReturnType = nil
273295
body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()")
274296
} else {
275-
abiReturnType = liftingInfo.valueToLift
276297
let liftExpr: String
277298
switch returnType {
278299
case .closure(let signature, _):
@@ -288,25 +309,24 @@ public struct ImportTS {
288309
}
289310
}
290311

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

298318
// Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise
299319
let innerBody = body
300320
body = CodeFragmentPrinter()
301321

302322
let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }"
303-
if originalReturnType == .void {
323+
if returnType == .void {
304324
let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }"
305325
body.write(
306326
"try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
307327
)
308328
} else {
309-
let resolveSwiftType = originalReturnType.closureSwiftType
329+
let resolveSwiftType = returnType.closureSwiftType
310330
let resolveFactory =
311331
"makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }"
312332
body.write(
@@ -318,19 +338,11 @@ public struct ImportTS {
318338
}
319339
body.write("}")
320340

321-
if originalReturnType != .void {
341+
if returnType != .void {
322342
body.write("return resolved")
323343
}
324344
}
325345

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-
334346
func renderImportDecl() -> DeclSyntax {
335347
let printer = CodeFragmentPrinter()
336348
SwiftCodePattern.buildExternFunctionDecl(
@@ -408,26 +420,17 @@ public struct ImportTS {
408420
_ function: ImportedFunctionSkeleton,
409421
topLevelDecls: inout [DeclSyntax]
410422
) 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
414423
let builder = try CallJSEmission(
415424
moduleName: moduleName,
416425
abiName: function.abiName(context: nil),
417-
returnType: abiReturnType
426+
effects: function.effects,
427+
returnType: function.returnType
418428
)
419-
if function.effects.isAsync {
420-
builder.prependClosureCallbackParams()
421-
}
422429
for param in function.parameters {
423430
try builder.lowerParameter(param: param)
424431
}
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-
}
432+
try builder.call()
433+
try builder.liftReturnValue()
431434
topLevelDecls.append(builder.renderImportDecl())
432435
return [
433436
builder.renderThunkDecl(
@@ -445,25 +448,18 @@ public struct ImportTS {
445448
var decls: [DeclSyntax] = []
446449

447450
func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] {
448-
let abiReturnType: BridgeType = method.effects.isAsync ? .void : method.returnType
449451
let builder = try CallJSEmission(
450452
moduleName: moduleName,
451453
abiName: method.abiName(context: type),
452-
returnType: abiReturnType
454+
effects: method.effects,
455+
returnType: method.returnType
453456
)
454-
if method.effects.isAsync {
455-
builder.prependClosureCallbackParams()
456-
}
457457
try builder.lowerParameter(param: selfParameter)
458458
for param in method.parameters {
459459
try builder.lowerParameter(param: param)
460460
}
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-
}
461+
try builder.call()
462+
try builder.liftReturnValue()
467463
topLevelDecls.append(builder.renderImportDecl())
468464
return [
469465
builder.renderThunkDecl(
@@ -477,20 +473,17 @@ public struct ImportTS {
477473

478474
func renderStaticMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] {
479475
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-
}
476+
let builder = try CallJSEmission(
477+
moduleName: moduleName,
478+
abiName: abiName,
479+
effects: method.effects,
480+
returnType: method.returnType
481+
)
485482
for param in method.parameters {
486483
try builder.lowerParameter(param: param)
487484
}
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-
}
485+
try builder.call()
486+
try builder.liftReturnValue()
494487
topLevelDecls.append(builder.renderImportDecl())
495488
return [
496489
builder.renderThunkDecl(
@@ -506,6 +499,7 @@ public struct ImportTS {
506499
let builder = try CallJSEmission(
507500
moduleName: moduleName,
508501
abiName: constructor.abiName(context: type),
502+
effects: Effects(isAsync: false, isThrows: true),
509503
returnType: .jsObject(nil)
510504
)
511505
for param in constructor.parameters {
@@ -527,6 +521,7 @@ public struct ImportTS {
527521
let builder = try CallJSEmission(
528522
moduleName: moduleName,
529523
abiName: getter.abiName(context: type),
524+
effects: Effects(isAsync: false, isThrows: true),
530525
returnType: getter.type
531526
)
532527
try builder.lowerParameter(param: selfParameter)
@@ -546,6 +541,7 @@ public struct ImportTS {
546541
let builder = try CallJSEmission(
547542
moduleName: moduleName,
548543
abiName: setter.abiName(context: type),
544+
effects: Effects(isAsync: false, isThrows: true),
549545
returnType: .void
550546
)
551547
let newValue = Parameter(label: nil, name: "newValue", type: setter.type)

0 commit comments

Comments
 (0)