Skip to content

Commit d628d2d

Browse files
committed
BridgeJS: Closure support review comments
1 parent 3d17fda commit d628d2d

File tree

12 files changed

+888
-938
lines changed

12 files changed

+888
-938
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class ExportSwift {
2828
private var exportedProtocolNameByKey: [String: String] = [:]
2929
private var typeDeclResolver: TypeDeclResolver = TypeDeclResolver()
3030
private let enumCodegen: EnumCodegen = EnumCodegen()
31-
private lazy var closureCodegen = ClosureCodegen(moduleName: moduleName)
31+
private let closureCodegen = ClosureCodegen()
3232

3333
public init(progress: ProgressReporting, moduleName: String) {
3434
self.progress = progress
@@ -1354,6 +1354,7 @@ public class ExportSwift {
13541354
ClosureSignature(
13551355
parameters: parameters,
13561356
returnType: returnType,
1357+
moduleName: moduleName,
13571358
isAsync: isAsync,
13581359
isThrows: isThrows
13591360
)
@@ -1477,7 +1478,6 @@ public class ExportSwift {
14771478
}
14781479
decls.append(Self.prelude)
14791480

1480-
// Collect all unique closure signatures
14811481
var closureSignatures: Set<ClosureSignature> = []
14821482
for function in exportedFunctions {
14831483
collectClosureSignatures(from: function.parameters, into: &closureSignatures)
@@ -1817,20 +1817,14 @@ public class ExportSwift {
18171817
}
18181818

18191819
private struct ClosureCodegen {
1820-
let moduleName: String
1821-
18221820
func generateOptionalParameterLowering(signature: ClosureSignature) throws -> String {
18231821
var lines: [String] = []
18241822

18251823
for (index, paramType) in signature.parameters.enumerated() {
18261824
guard case .optional(let wrappedType) = paramType else {
18271825
continue
18281826
}
1829-
18301827
let paramName = "param\(index)"
1831-
1832-
// Use bridgeJSLowerParameterWithRetain for heap objects in escaping closures
1833-
// to ensure proper ownership transfer
18341828
if case .swiftHeapObject = wrappedType {
18351829
lines.append(
18361830
"let (\(paramName)IsSome, \(paramName)Value) = \(paramName).bridgeJSLowerParameterWithRetain()"
@@ -1859,7 +1853,7 @@ public class ExportSwift {
18591853
let closureType = "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)"
18601854

18611855
var invokeParams: [(name: String, type: String)] = [("_", "Int32")]
1862-
var invokeCallArgs: [String] = ["owner.callbackId"]
1856+
var invokeCallArgs: [String] = ["callback.bridgeJSLowerParameter()"]
18631857

18641858
for (index, paramType) in signature.parameters.enumerated() {
18651859
let paramName = "param\(index)"
@@ -1920,7 +1914,7 @@ public class ExportSwift {
19201914
"""
19211915
}
19221916

1923-
let externName = "invoke_js_callback_\(mangledName.lowercased())"
1917+
let externName = "invoke_js_callback_\(signature.moduleName)_\(mangledName)"
19241918
let optionalLoweringCode = try generateOptionalParameterLowering(signature: signature)
19251919

19261920
return """
@@ -1938,8 +1932,8 @@ public class ExportSwift {
19381932
}
19391933
19401934
static func bridgeJSLift(_ callbackId: Int32) -> \(raw: closureType) {
1941-
let owner = _JSCallbackOwner(callbackId: callbackId)
1942-
return { [owner] \(raw: signature.parameters.indices.map { "param\($0)" }.joined(separator: ", ")) in
1935+
let callback = JSObject.bridgeJSLiftParameter(callbackId)
1936+
return { [callback] \(raw: signature.parameters.indices.map { "param\($0)" }.joined(separator: ", ")) in
19431937
#if arch(wasm32)
19441938
@_extern(wasm, module: "bjs", name: "\(raw: externName)")
19451939
func _invoke(\(raw: invokeSignature)) -> \(raw: invokeReturnType)
@@ -1955,7 +1949,7 @@ public class ExportSwift {
19551949

19561950
func renderClosureInvokeHandler(signature: ClosureSignature) throws -> DeclSyntax {
19571951
let boxClassName = "_BJS_ClosureBox_\(signature.mangleName)"
1958-
let abiName = "invoke_swift_closure_\(signature.mangleName.lowercased())"
1952+
let abiName = "invoke_swift_closure_\(signature.moduleName)_\(signature.mangleName)"
19591953

19601954
var abiParams: [(name: String, type: String)] = [("boxPtr", "UnsafeMutableRawPointer")]
19611955
var liftedParams: [String] = []
@@ -2471,14 +2465,12 @@ public class ExportSwift {
24712465
return decls
24722466
}
24732467

2474-
/// Collects all closure signatures from function parameters
24752468
private func collectClosureSignatures(from parameters: [Parameter], into signatures: inout Set<ClosureSignature>) {
24762469
for param in parameters {
24772470
collectClosureSignatures(from: param.type, into: &signatures)
24782471
}
24792472
}
24802473

2481-
/// Collects all closure signatures from a bridge type
24822474
private func collectClosureSignatures(from type: BridgeType, into signatures: inout Set<ClosureSignature>) {
24832475
switch type {
24842476
case .closure(let signature):
@@ -2933,7 +2925,6 @@ extension BridgeType {
29332925
case .namespaceEnum:
29342926
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
29352927
case .closure:
2936-
// Closures are returned as UnsafeMutableRawPointer (box pointer)
29372928
return .swiftHeapObject
29382929
}
29392930
}

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -696,53 +696,29 @@ struct BridgeJSLink {
696696
}
697697
printer.write("}")
698698

699-
var closureSignatures: Set<ClosureSignature> = []
700699
for skeleton in exportedSkeletons {
700+
var closureSignatures: Set<ClosureSignature> = []
701701
collectClosureSignatures(from: skeleton, into: &closureSignatures)
702-
}
703702

704-
var classToModule: [String: String] = [:]
705-
for skeleton in exportedSkeletons {
706-
for klass in skeleton.classes {
707-
classToModule[klass.name] = skeleton.moduleName
708-
}
709-
}
703+
guard !closureSignatures.isEmpty else { continue }
710704

711-
for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) {
712-
let invokeFuncName = "invoke_js_callback_\(signature.mangleName.lowercased())"
713-
printer.write(
714-
lines: generateInvokeFunction(
715-
signature: signature,
716-
functionName: invokeFuncName,
717-
classToModule: classToModule
705+
for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) {
706+
let invokeFuncName = "invoke_js_callback_\(skeleton.moduleName)_\(signature.mangleName)"
707+
printer.write(
708+
lines: generateInvokeFunction(
709+
signature: signature,
710+
functionName: invokeFuncName
711+
)
718712
)
719-
)
720713

721-
let lowerFuncName = "lower_closure_\(signature.mangleName.lowercased())"
722-
printer.write(
723-
lines: generateLowerClosureFunction(
724-
signature: signature,
725-
functionName: lowerFuncName
726-
)
727-
)
728-
}
729-
730-
if !closureSignatures.isEmpty {
731-
printer.nextLine()
732-
printer.write("bjs[\"release_js_callback\"] = function(id) {")
733-
printer.indent {
734-
printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(id);")
735-
}
736-
printer.write("};")
737-
738-
printer.nextLine()
739-
printer.write("bjs[\"release_swift_closure\"] = function(boxPtr) {")
740-
printer.indent {
714+
let lowerFuncName = "lower_closure_\(skeleton.moduleName)_\(signature.mangleName)"
741715
printer.write(
742-
"\(JSGlueVariableScope.reservedInstance).exports._release_swift_closure(boxPtr);"
716+
lines: generateLowerClosureFunction(
717+
signature: signature,
718+
functionName: lowerFuncName
719+
)
743720
)
744721
}
745-
printer.write("};")
746722
}
747723
}
748724
}
@@ -793,8 +769,7 @@ struct BridgeJSLink {
793769

794770
private func generateInvokeFunction(
795771
signature: ClosureSignature,
796-
functionName: String,
797-
classToModule: [String: String]
772+
functionName: String
798773
) -> [String] {
799774
let printer = CodeFragmentPrinter()
800775
let scope = JSGlueVariableScope()
@@ -889,7 +864,7 @@ struct BridgeJSLink {
889864

890865
// Call the Swift invoke function
891866
let invokeCall =
892-
"\(JSGlueVariableScope.reservedInstance).exports.invoke_swift_closure_\(signature.mangleName.lowercased())(\(invokeArgs.joined(separator: ", ")))"
867+
"\(JSGlueVariableScope.reservedInstance).exports.invoke_swift_closure_\(signature.moduleName)_\(signature.mangleName)(\(invokeArgs.joined(separator: ", ")))"
893868

894869
let returnFragment = try! IntrinsicJSFragment.closureLiftReturn(type: signature.returnType)
895870
_ = returnFragment.printCode([invokeCall], scope, printer, cleanupCode)

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,14 +1298,13 @@ struct IntrinsicJSFragment: Sendable {
12981298
let base = fullName.components(separatedBy: ".").last ?? fullName
12991299
return .associatedEnumLiftReturn(enumBase: base)
13001300
case .closure(let signature):
1301+
let lowerFuncName = "lower_closure_\(signature.moduleName)_\(signature.mangleName)"
13011302
return IntrinsicJSFragment(
1302-
parameters: ["closurePtr"],
1303+
parameters: ["boxPtr"],
13031304
printCode: { arguments, scope, printer, cleanupCode in
1304-
let closurePtr = arguments[0]
1305-
let lowerFuncName = "lower_closure_\(signature.mangleName.lowercased())"
1306-
let resultVar = scope.variable("closure")
1307-
printer.write("const \(resultVar) = bjs[\"\(lowerFuncName)\"](\(closurePtr));")
1308-
return [resultVar]
1305+
let boxPtr = arguments[0]
1306+
printer.write("return bjs[\"\(lowerFuncName)\"](\(boxPtr));")
1307+
return []
13091308
}
13101309
)
13111310
case .namespaceEnum(let string):

Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,34 @@ public enum BridgeContext: Sendable {
7676
public struct ClosureSignature: Codable, Equatable, Hashable, Sendable {
7777
public let parameters: [BridgeType]
7878
public let returnType: BridgeType
79+
/// Simplified Swift ABI-style mangling with module prefix
80+
// <moduleLength><module> + params + _ + return
81+
// Examples:
82+
// - 4MainSS_Si (Main module, String->Int)
83+
// - 6MyAppSiSi_y (MyApp module, Int,Int->Void)
84+
public let mangleName: String
7985
public let isAsync: Bool
8086
public let isThrows: Bool
81-
public let mangleName: String
87+
public let moduleName: String
8288

83-
public init(parameters: [BridgeType], returnType: BridgeType, isAsync: Bool = false, isThrows: Bool = false) {
89+
public init(
90+
parameters: [BridgeType],
91+
returnType: BridgeType,
92+
moduleName: String,
93+
isAsync: Bool = false,
94+
isThrows: Bool = false
95+
) {
8496
self.parameters = parameters
8597
self.returnType = returnType
98+
self.moduleName = moduleName
8699
self.isAsync = isAsync
87100
self.isThrows = isThrows
88-
89101
let paramPart =
90102
parameters.isEmpty
91-
? "Void"
92-
: parameters.map { $0.mangleTypeName }.joined(separator: "_")
93-
let returnPart = returnType.mangleTypeName
94-
self.mangleName = "\(paramPart)_To_\(returnPart)"
103+
? "y"
104+
: parameters.map { $0.mangleTypeName }.joined()
105+
let signaturePart = "\(paramPart)_\(returnType.mangleTypeName)"
106+
self.mangleName = "\(moduleName.count)\(moduleName)\(signaturePart)"
95107
}
96108
}
97109

@@ -602,31 +614,36 @@ extension BridgeType {
602614
return false
603615
}
604616

605-
/// Generates a mangled name for use in closure type names
606-
/// Examples: "String", "Int", "MyClass", "Bool"
617+
/// Simplified Swift ABI-style mangled name
618+
/// https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst#types
607619
public var mangleTypeName: String {
608620
switch self {
609-
case .int: return "Int"
610-
case .float: return "Float"
611-
case .double: return "Double"
612-
case .string: return "String"
613-
case .bool: return "Bool"
614-
case .void: return "Void"
621+
case .int: return "Si"
622+
case .float: return "Sf"
623+
case .double: return "Sd"
624+
case .string: return "SS"
625+
case .bool: return "Sb"
626+
case .void: return "y"
615627
case .jsObject(let name):
616-
return name ?? "JSObject"
628+
let typeName = name ?? "JSObject"
629+
return "\(typeName.count)\(typeName)C"
617630
case .swiftHeapObject(let name):
618-
return name
631+
return "\(name.count)\(name)C"
619632
case .optional(let wrapped):
620-
return "Optional\(wrapped.mangleTypeName)"
633+
return "Sq\(wrapped.mangleTypeName)"
621634
case .caseEnum(let name),
622635
.rawValueEnum(let name, _),
623636
.associatedValueEnum(let name),
624637
.namespaceEnum(let name):
625-
return name
638+
return "\(name.count)\(name)O"
626639
case .swiftProtocol(let name):
627-
return name
640+
return "\(name.count)\(name)P"
628641
case .closure(let signature):
629-
return "Closure_\(signature.mangleName)"
642+
let params =
643+
signature.parameters.isEmpty
644+
? "y"
645+
: signature.parameters.map { $0.mangleTypeName }.joined()
646+
return "K\(params)_\(signature.returnType.mangleTypeName)"
630647
}
631648
}
632649

0 commit comments

Comments
 (0)