Skip to content

Commit bcea48f

Browse files
committed
BridgeJS: Qualify generated JS helper names by module
1 parent a4f1575 commit bcea48f

35 files changed

Lines changed: 630 additions & 867 deletions

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ public class ExportSwift {
9292
}
9393

9494
withSpan("Render Generic Bridgeable Conformances") { [self] in
95-
// Emitted unconditionally: a module cannot know whether a dependent
96-
// module passes its types to a generic imported function.
9795
let genericConformanceCodegen = GenericConformanceCodegen()
9896
for entry in skeleton.genericBridgeableTypeEntries {
9997
decls.append(contentsOf: genericConformanceCodegen.renderConformance(typeName: entry.swiftName))
@@ -886,8 +884,6 @@ public class ExportSwift {
886884

887885
// MARK: - GenericConformanceCodegen
888886

889-
/// Renders `BridgedSwiftGenericBridgeable` conformances for `@JS` types so they
890-
/// can be used as the generic argument of a generic imported `@JSFunction`.
891887
struct GenericConformanceCodegen {
892888
func renderConformance(typeName: String) -> [DeclSyntax] {
893889
let printer = CodeFragmentPrinter()
@@ -904,14 +900,6 @@ struct GenericConformanceCodegen {
904900

905901
// MARK: - GenericTypeRegistrationCodegen
906902

907-
/// Renders the `bjs_<Module>_register_type_handles` wasm export: it lowers each
908-
/// registered type's `bridgeJSTypeID` into a buffer, in the canonical order of
909-
/// `BridgeJSSkeleton.typeRegistrationEntries`, and passes it to the JS import
910-
/// hook of the same name, which pairs the IDs with its codec array by index.
911-
///
912-
/// Only the module's own `@JS` types are listed; the core (primitive) handles are
913-
/// registered once by the JavaScriptKit library itself
914-
/// (`_bjs_core_register_type_handles`).
915903
public struct GenericTypeRegistrationCodegen {
916904
public init() {}
917905

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 36 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,6 @@ public struct BridgeJSLink {
348348
declarations.append(" return;")
349349
declarations.append(" }")
350350
declarations.append(" __bjs_typeHandlesRegistered = true;")
351-
// The core (primitive) handles live in the JavaScriptKit library, so
352-
// they are registered once here rather than by every module.
353351
declarations.append(
354352
" \(JSGlueVariableScope.reservedInstance).exports[\"\(ABINameGenerator.coreTypeRegistrationFunctionName)\"]();"
355353
)
@@ -403,7 +401,6 @@ public struct BridgeJSLink {
403401
printer.write(lines: lines)
404402
}
405403

406-
/// A print context detached from any thunk, used for codec literal emission.
407404
private func makeCodecPrintContext(printer: CodeFragmentPrinter) -> IntrinsicJSFragment.PrintCodeContext {
408405
IntrinsicJSFragment.PrintCodeContext(
409406
scope: JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry),
@@ -413,16 +410,10 @@ public struct BridgeJSLink {
413410
)
414411
}
415412

416-
/// Returns the module-scope codec helper for one bridgeable type, declaring
417-
/// it if this is the first reference.
418-
///
419-
/// The registration table and the container combinators' element positions
420-
/// go through the same helper, so a type's stack ABI is described once.
421413
private func genericCodecReference(type: BridgeType, into printer: CodeFragmentPrinter) throws -> String {
422414
try ContainerCodecJS.codecExpression(for: type, context: makeCodecPrintContext(printer: printer))
423415
}
424416

425-
/// Pairs the type IDs Swift pushed with codecs in the matching skeleton order.
426417
private func writeTypeHandleRegistrationBody(into printer: CodeFragmentPrinter) {
427418
printer.write(
428419
"const typeIds = new Int32Array(\(JSGlueVariableScope.reservedMemory).buffer, base >>> 0, count >>> 0);"
@@ -434,11 +425,6 @@ public struct BridgeJSLink {
434425
printer.write("}")
435426
}
436427

437-
/// Installs the `bjs_core_register_type_handles` hook. The core handles are
438-
/// owned by the JavaScriptKit library rather than by generated code, so the
439-
/// wasm import exists in every binary that links JavaScriptKit and the hook
440-
/// is always installed; without generics anywhere in the build it is a no-op
441-
/// and the registration export is never called.
442428
private func generateCoreTypeRegistrationHook(into printer: CodeFragmentPrinter) throws {
443429
let hookName = ABINameGenerator.coreTypeRegistrationFunctionName
444430
guard hasGenerics else {
@@ -448,8 +434,6 @@ public struct BridgeJSLink {
448434
try ContainerCodecJS.registerPrimitiveCodecs(context: makeCodecPrintContext(printer: printer))
449435
printer.write("bjs[\"\(hookName)\"] = function(base, count) {")
450436
printer.indent {
451-
// Same canonical order as `_bjs_core_register_type_handles` in the
452-
// JavaScriptKit library.
453437
printer.write("const codecs = [")
454438
printer.indent {
455439
for primitive in BridgeType.genericBridgeablePrimitives {
@@ -462,10 +446,6 @@ public struct BridgeJSLink {
462446
printer.write("}")
463447
}
464448

465-
/// Installs the per-module `bjs_<Module>_register_type_handles` import
466-
/// hooks. A module with a registration function always carries the wasm
467-
/// import, so a hook is always installed; without generics anywhere in the
468-
/// build it is a no-op and the registration export is never called.
469449
private func generateTypeRegistrationHooks(into printer: CodeFragmentPrinter) throws {
470450
try generateCoreTypeRegistrationHook(into: printer)
471451
for skeleton in skeletons {
@@ -477,7 +457,6 @@ public struct BridgeJSLink {
477457
}
478458
printer.write("bjs[\"\(hookName)\"] = function(base, count) {")
479459
try printer.indent {
480-
// Same order as the module's Swift registration function.
481460
let codecNames = try moduleEntries.map {
482461
try genericCodecReference(type: $0.bridgeType, into: printer)
483462
}
@@ -496,7 +475,9 @@ public struct BridgeJSLink {
496475

497476
private func generateAddImports(needsImportsObject: Bool) throws -> CodeFragmentPrinter {
498477
let printer = CodeFragmentPrinter()
499-
let allStructs = skeletons.compactMap { $0.exported?.structs }.flatMap { $0 }
478+
let allStructs = skeletons.flatMap { unified in
479+
(unified.exported?.structs ?? []).map { (moduleName: unified.moduleName, structDef: $0) }
480+
}
500481
printer.write("return {")
501482
try printer.indent {
502483
printer.write(lines: [
@@ -644,19 +625,20 @@ public struct BridgeJSLink {
644625
}
645626
printer.write("}")
646627
if !allStructs.isEmpty {
647-
for structDef in allStructs {
628+
for (moduleName, structDef) in allStructs {
629+
let key = HelperNaming.qualified(base: structDef.abiName, module: moduleName)
648630
printer.write("bjs[\"swift_js_struct_lower_\(structDef.abiName)\"] = function(objectId) {")
649631
printer.indent {
650632
printer.write(
651-
"\(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName).lower(\(JSGlueVariableScope.reservedSwift).memory.getObject(objectId));"
633+
"\(JSGlueVariableScope.reservedStructHelpers).\(key).lower(\(JSGlueVariableScope.reservedSwift).memory.getObject(objectId));"
652634
)
653635
}
654636
printer.write("}")
655637

656638
printer.write("bjs[\"swift_js_struct_lift_\(structDef.abiName)\"] = function() {")
657639
printer.indent {
658640
printer.write(
659-
"const value = \(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName).lift();"
641+
"const value = \(JSGlueVariableScope.reservedStructHelpers).\(key).lift();"
660642
)
661643
printer.write("return \(JSGlueVariableScope.reservedSwift).memory.retain(value);")
662644
}
@@ -1229,12 +1211,18 @@ public struct BridgeJSLink {
12291211

12301212
let bodyPrinter = CodeFragmentPrinter()
12311213
let allStructs = exportedSkeletons.flatMap { $0.structs }
1232-
for structDef in allStructs {
1214+
for (moduleName, structDef) in skeletons.flatMap({ unified in
1215+
(unified.exported?.structs ?? []).map { (unified.moduleName, $0) }
1216+
}) {
12331217
let structPrinter = CodeFragmentPrinter()
12341218
let structScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry)
1235-
let fragment = IntrinsicJSFragment.structHelper(structDefinition: structDef, allStructs: allStructs)
1219+
let fragment = IntrinsicJSFragment.structHelper(
1220+
structDefinition: structDef,
1221+
allStructs: allStructs,
1222+
moduleName: moduleName
1223+
)
12361224
_ = try fragment.printCode(
1237-
[structDef.abiName],
1225+
[],
12381226
IntrinsicJSFragment.PrintCodeContext(
12391227
scope: structScope,
12401228
printer: structPrinter,
@@ -1245,13 +1233,16 @@ public struct BridgeJSLink {
12451233
bodyPrinter.write(lines: structPrinter.lines)
12461234
}
12471235

1248-
let allAssocEnums = exportedSkeletons.flatMap {
1249-
$0.enums.filter { $0.enumType == .associatedValue }
1250-
}
1251-
for enumDef in allAssocEnums {
1236+
for (moduleName, enumDef) in skeletons.flatMap({ unified in
1237+
(unified.exported?.enums ?? []).filter { $0.enumType == .associatedValue }
1238+
.map { (unified.moduleName, $0) }
1239+
}) {
12521240
let enumPrinter = CodeFragmentPrinter()
12531241
let enumScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry)
1254-
let fragment = IntrinsicJSFragment.associatedValueEnumHelperFactory(enumDefinition: enumDef)
1242+
let fragment = IntrinsicJSFragment.associatedValueEnumHelperFactory(
1243+
enumDefinition: enumDef,
1244+
moduleName: moduleName
1245+
)
12551246
_ = try fragment.printCode(
12561247
[enumDef.valuesName],
12571248
IntrinsicJSFragment.PrintCodeContext(
@@ -1271,13 +1262,6 @@ public struct BridgeJSLink {
12711262
printer.nextLine()
12721263
}
12731264

1274-
// The named codec helpers come after the intrinsics because they are
1275-
// built out of the combinators and the primitive codec table, and
1276-
// before everything that uses them: they are hoisted here so that no
1277-
// call site ever composes a codec. Helpers that delegate to the
1278-
// `structHelpers` / `enumHelpers` tables only read those tables when
1279-
// called, so declaring them ahead of the tables being populated is
1280-
// fine.
12811265
if intrinsicRegistry.hasNamedCodecs {
12821266
printer.write(lines: intrinsicRegistry.emitNamedCodecLines())
12831267
printer.nextLine()
@@ -1380,12 +1364,6 @@ public struct BridgeJSLink {
13801364
return (outputJs, outputDts)
13811365
}
13821366

1383-
/// Maps every type name a `BridgeType` can carry to the module that declares
1384-
/// it, so identifiers minted from type names can be module-qualified.
1385-
///
1386-
/// A name declared by two modules is a pre-existing ambiguity in the
1387-
/// skeleton format (`BridgeType` carries only the name), so the first
1388-
/// declaration wins, which keeps the output deterministic.
13891367
private func collectTypeOwnerModules() -> [String: String] {
13901368
var result: [String: String] = [:]
13911369
func record(_ name: String, _ moduleName: String) {
@@ -1412,24 +1390,20 @@ public struct BridgeJSLink {
14121390
record(protocolDef.name, moduleName)
14131391
}
14141392
}
1415-
for file in unified.imported?.children ?? [] {
1416-
for type in file.types {
1417-
record(type.name, moduleName)
1418-
}
1419-
}
14201393
}
14211394
return result
14221395
}
14231396

14241397
private func enumHelperAssignments() -> CodeFragmentPrinter {
14251398
let printer = CodeFragmentPrinter()
14261399

1427-
for skeleton in skeletons.compactMap(\.exported) {
1400+
for unified in skeletons {
1401+
guard let skeleton = unified.exported else { continue }
14281402
for enumDef in skeleton.enums where enumDef.enumType == .associatedValue {
1429-
printer.write(
1430-
"const \(enumDef.name)Helpers = __bjs_create\(enumDef.valuesName)Helpers();"
1431-
)
1432-
printer.write("\(JSGlueVariableScope.reservedEnumHelpers).\(enumDef.name) = \(enumDef.name)Helpers;")
1403+
let key = HelperNaming.qualified(base: enumDef.name, module: unified.moduleName)
1404+
let local = HelperNaming.helperConstant(key)
1405+
printer.write("const \(local) = \(HelperNaming.enumHelperFactory(key))();")
1406+
printer.write("\(JSGlueVariableScope.reservedEnumHelpers).\(key) = \(local);")
14331407
printer.nextLine()
14341408
}
14351409
}
@@ -1440,14 +1414,13 @@ public struct BridgeJSLink {
14401414
private func structHelperAssignments() -> CodeFragmentPrinter {
14411415
let printer = CodeFragmentPrinter()
14421416

1443-
for skeleton in skeletons.compactMap(\.exported) {
1417+
for unified in skeletons {
1418+
guard let skeleton = unified.exported else { continue }
14441419
for structDef in skeleton.structs {
1445-
printer.write(
1446-
"const \(structDef.abiName)Helpers = __bjs_create\(structDef.abiName)Helpers();"
1447-
)
1448-
printer.write(
1449-
"\(JSGlueVariableScope.reservedStructHelpers).\(structDef.abiName) = \(structDef.abiName)Helpers;"
1450-
)
1420+
let key = HelperNaming.qualified(base: structDef.abiName, module: unified.moduleName)
1421+
let local = HelperNaming.helperConstant(key)
1422+
printer.write("const \(local) = \(HelperNaming.structHelperFactory(key))();")
1423+
printer.write("\(JSGlueVariableScope.reservedStructHelpers).\(key) = \(local);")
14511424
printer.nextLine()
14521425
}
14531426
}
@@ -2593,8 +2566,6 @@ extension BridgeJSLink {
25932566

25942567
func declareGenericCodecs(genericParameters: [String]) {
25952568
if !genericParameters.isEmpty {
2596-
// Generic call sites instantiate the shared container codec
2597-
// combinators with the codecs resolved from type IDs.
25982569
ContainerCodecJS.registerCombinators(scope: scope)
25992570
}
26002571
for genericParam in genericParameters {

0 commit comments

Comments
 (0)