Skip to content

Commit 8d4472e

Browse files
committed
Use JSTypedClosure without wrapping the result value
1 parent 3044b3a commit 8d4472e

File tree

7 files changed

+982
-104
lines changed

7 files changed

+982
-104
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -190,24 +190,68 @@ public struct ClosureCodegen {
190190
walker.walk(skeleton)
191191
var closureSignatures = walker.visitor.signatures
192192

193-
// When any async import exists, inject a (JSValue) -> Void closure signature
194-
// so the closure infrastructure generates the make/invoke exports needed by
195-
// _bjs_awaitPromise's resolve/reject callbacks.
193+
// When async imports exist, inject closure signatures for the typed resolve
194+
// and reject callbacks used by _bjs_awaitPromise.
195+
// - Reject always uses (JSValue) -> Void
196+
// - Resolve uses a typed closure matching the return type (or () -> Void for void)
196197
if let imported = skeleton.imported {
197-
let hasAsyncImport = imported.children.contains { file in
198-
file.functions.contains(where: { $0.effects.isAsync })
199-
|| file.types.contains(where: { type in
200-
type.methods.contains(where: { $0.effects.isAsync })
201-
})
202-
}
203-
if hasAsyncImport {
204-
closureSignatures.insert(
205-
ClosureSignature(
206-
parameters: [.jsValue],
207-
returnType: .void,
208-
moduleName: skeleton.moduleName
198+
for file in imported.children {
199+
for function in file.functions where function.effects.isAsync {
200+
// Reject callback
201+
closureSignatures.insert(
202+
ClosureSignature(
203+
parameters: [.jsValue],
204+
returnType: .void,
205+
moduleName: skeleton.moduleName
206+
)
209207
)
210-
)
208+
// Resolve callback (typed per return type)
209+
if function.returnType == .void {
210+
closureSignatures.insert(
211+
ClosureSignature(
212+
parameters: [],
213+
returnType: .void,
214+
moduleName: skeleton.moduleName
215+
)
216+
)
217+
} else {
218+
closureSignatures.insert(
219+
ClosureSignature(
220+
parameters: [function.returnType],
221+
returnType: .void,
222+
moduleName: skeleton.moduleName
223+
)
224+
)
225+
}
226+
}
227+
for type in file.types {
228+
for method in type.methods where method.effects.isAsync {
229+
closureSignatures.insert(
230+
ClosureSignature(
231+
parameters: [.jsValue],
232+
returnType: .void,
233+
moduleName: skeleton.moduleName
234+
)
235+
)
236+
if method.returnType == .void {
237+
closureSignatures.insert(
238+
ClosureSignature(
239+
parameters: [],
240+
returnType: .void,
241+
moduleName: skeleton.moduleName
242+
)
243+
)
244+
} else {
245+
closureSignatures.insert(
246+
ClosureSignature(
247+
parameters: [method.returnType],
248+
returnType: .void,
249+
moduleName: skeleton.moduleName
250+
)
251+
)
252+
}
253+
}
254+
}
211255
}
212256
}
213257

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -291,19 +291,26 @@ public struct ImportTS {
291291
func liftAsyncReturnValue(originalReturnType: BridgeType) {
292292
// For async imports, the extern function takes leading `resolveRef: Int32, rejectRef: Int32`
293293
// and returns void. The JS side calls the resolve/reject closures when the Promise settles.
294+
// The resolve closure is typed to match the return type, so the ABI conversion is handled
295+
// by the existing closure codegen infrastructure — no manual JSValue-to-type switch needed.
294296
abiReturnType = nil
295297

296298
// Wrap the existing body (parameter lowering + extern call) in _bjs_awaitPromise
297299
let innerBody = body
298300
body = CodeFragmentPrinter()
299301

302+
let rejectFactory = "makeRejectClosure: { JSTypedClosure<(JSValue) -> Void>($0) }"
300303
if originalReturnType == .void {
304+
let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }"
301305
body.write(
302-
"_ = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in"
306+
"try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
303307
)
304308
} else {
309+
let resolveSwiftType = originalReturnType.closureSwiftType
310+
let resolveFactory =
311+
"makeResolveClosure: { JSTypedClosure<(\(resolveSwiftType)) -> Void>($0) }"
305312
body.write(
306-
"let resolved = try await _bjs_awaitPromise(makeClosure: { JSTypedClosure($0) }) { resolveRef, rejectRef in"
313+
"let resolved = try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
307314
)
308315
}
309316
body.indent {
@@ -312,30 +319,7 @@ public struct ImportTS {
312319
body.write("}")
313320

314321
if originalReturnType != .void {
315-
let liftExpr: String
316-
switch originalReturnType {
317-
case .double:
318-
liftExpr = "Double(resolved.number!)"
319-
case .float:
320-
liftExpr = "Float(resolved.number!)"
321-
case .integer:
322-
liftExpr = "Int(resolved.number!)"
323-
case .string:
324-
liftExpr = "resolved.string!"
325-
case .bool:
326-
liftExpr = "resolved.boolean!"
327-
case .jsObject(let name):
328-
if let name {
329-
liftExpr = "\(name)(unsafelyWrapping: resolved.object!)"
330-
} else {
331-
liftExpr = "resolved.object!"
332-
}
333-
case .jsValue:
334-
liftExpr = "resolved"
335-
default:
336-
liftExpr = "resolved.object!"
337-
}
338-
body.write("return \(liftExpr)")
322+
body.write("return resolved")
339323
}
340324
}
341325

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -641,22 +641,65 @@ public struct BridgeJSLink {
641641
walker.walk(unified)
642642
var closureSignatures = walker.visitor.signatures
643643

644-
// Inject (JSValue) -> Void closure signature when async imports exist
644+
// Inject closure signatures for async import resolve/reject callbacks
645645
if let imported = unified.imported {
646-
let hasAsyncImport = imported.children.contains { file in
647-
file.functions.contains(where: { $0.effects.isAsync })
648-
|| file.types.contains(where: { type in
649-
type.methods.contains(where: { $0.effects.isAsync })
650-
})
651-
}
652-
if hasAsyncImport {
653-
closureSignatures.insert(
654-
ClosureSignature(
655-
parameters: [.jsValue],
656-
returnType: .void,
657-
moduleName: moduleName
646+
for file in imported.children {
647+
for function in file.functions where function.effects.isAsync {
648+
// Reject callback
649+
closureSignatures.insert(
650+
ClosureSignature(
651+
parameters: [.jsValue],
652+
returnType: .void,
653+
moduleName: moduleName
654+
)
658655
)
659-
)
656+
// Resolve callback (typed per return type)
657+
if function.returnType == .void {
658+
closureSignatures.insert(
659+
ClosureSignature(
660+
parameters: [],
661+
returnType: .void,
662+
moduleName: moduleName
663+
)
664+
)
665+
} else {
666+
closureSignatures.insert(
667+
ClosureSignature(
668+
parameters: [function.returnType],
669+
returnType: .void,
670+
moduleName: moduleName
671+
)
672+
)
673+
}
674+
}
675+
for type in file.types {
676+
for method in type.methods where method.effects.isAsync {
677+
closureSignatures.insert(
678+
ClosureSignature(
679+
parameters: [.jsValue],
680+
returnType: .void,
681+
moduleName: moduleName
682+
)
683+
)
684+
if method.returnType == .void {
685+
closureSignatures.insert(
686+
ClosureSignature(
687+
parameters: [],
688+
returnType: .void,
689+
moduleName: moduleName
690+
)
691+
)
692+
} else {
693+
closureSignatures.insert(
694+
ClosureSignature(
695+
parameters: [method.returnType],
696+
returnType: .void,
697+
moduleName: moduleName
698+
)
699+
)
700+
}
701+
}
702+
}
660703
}
661704
}
662705

0 commit comments

Comments
 (0)