Skip to content

Commit 5ede934

Browse files
committed
BridgeJS: Share named stack codecs across generated glue
1 parent afa6857 commit 5ede934

22 files changed

Lines changed: 1031 additions & 1227 deletions

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -413,22 +413,13 @@ public struct BridgeJSLink {
413413
)
414414
}
415415

416-
/// Emits a `{ lower, lift }` codec literal for one bridgeable type.
417-
/// `prefix` is prepended to the opening brace (e.g. an assignment) and
418-
/// `suffix` is appended to the closing brace (e.g. `","` in an array).
419-
private func appendGenericCodecLiteral(
420-
type: BridgeType,
421-
into printer: CodeFragmentPrinter,
422-
prefix: String = "",
423-
suffix: String = ","
424-
) throws {
425-
try ContainerCodecJS.writeCodecLiteral(
426-
type: type,
427-
into: printer,
428-
context: makeCodecPrintContext(printer: printer),
429-
prefix: prefix,
430-
suffix: suffix
431-
)
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.
421+
private func genericCodecReference(type: BridgeType, into printer: CodeFragmentPrinter) throws -> String {
422+
try ContainerCodecJS.codecExpression(for: type, context: makeCodecPrintContext(printer: printer))
432423
}
433424

434425
/// Pairs the type IDs Swift pushed with codecs in the matching skeleton order.
@@ -487,10 +478,13 @@ public struct BridgeJSLink {
487478
printer.write("bjs[\"\(hookName)\"] = function(base, count) {")
488479
try printer.indent {
489480
// Same order as the module's Swift registration function.
481+
let codecNames = try moduleEntries.map {
482+
try genericCodecReference(type: $0.bridgeType, into: printer)
483+
}
490484
printer.write("const codecs = [")
491-
try printer.indent {
492-
for entry in moduleEntries {
493-
try appendGenericCodecLiteral(type: entry.bridgeType, into: printer)
485+
printer.indent {
486+
for name in codecNames {
487+
printer.write("\(name),")
494488
}
495489
}
496490
printer.write("];")
@@ -1277,6 +1271,18 @@ public struct BridgeJSLink {
12771271
printer.nextLine()
12781272
}
12791273

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.
1281+
if intrinsicRegistry.hasNamedCodecs {
1282+
printer.write(lines: intrinsicRegistry.emitNamedCodecLines())
1283+
printer.nextLine()
1284+
}
1285+
12801286
printer.write(lines: bodyPrinter.lines)
12811287
}
12821288
printer.indent()
@@ -1367,12 +1373,54 @@ public struct BridgeJSLink {
13671373
}
13681374
}
13691375
}
1376+
intrinsicRegistry.typeOwnerModules = collectTypeOwnerModules()
13701377
let data = try collectLinkData()
13711378
let outputJs = try generateJavaScript(data: data)
13721379
let outputDts = generateTypeScript(data: data)
13731380
return (outputJs, outputDts)
13741381
}
13751382

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.
1389+
private func collectTypeOwnerModules() -> [String: String] {
1390+
var result: [String: String] = [:]
1391+
func record(_ name: String, _ moduleName: String) {
1392+
if result[name] == nil {
1393+
result[name] = moduleName
1394+
}
1395+
}
1396+
for unified in skeletons {
1397+
let moduleName = unified.moduleName
1398+
if let skeleton = unified.exported {
1399+
for structDef in skeleton.structs {
1400+
record(structDef.name, moduleName)
1401+
record(structDef.abiName, moduleName)
1402+
}
1403+
for klass in skeleton.classes {
1404+
record(klass.name, moduleName)
1405+
record(klass.abiName, moduleName)
1406+
}
1407+
for enumDef in skeleton.enums {
1408+
record(enumDef.name, moduleName)
1409+
record(enumDef.abiName, moduleName)
1410+
}
1411+
for protocolDef in skeleton.protocols {
1412+
record(protocolDef.name, moduleName)
1413+
}
1414+
}
1415+
for file in unified.imported?.children ?? [] {
1416+
for type in file.types {
1417+
record(type.name, moduleName)
1418+
}
1419+
}
1420+
}
1421+
return result
1422+
}
1423+
13761424
private func enumHelperAssignments() -> CodeFragmentPrinter {
13771425
let printer = CodeFragmentPrinter()
13781426

0 commit comments

Comments
 (0)