@@ -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