@@ -91,6 +91,15 @@ public class ExportSwift {
9191 }
9292 }
9393
94+ 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.
97+ let genericConformanceCodegen = GenericConformanceCodegen ( )
98+ for entry in skeleton. genericBridgeableTypeEntries {
99+ decls. append ( contentsOf: genericConformanceCodegen. renderConformance ( typeName: entry. swiftName) )
100+ }
101+ }
102+
94103 try withSpan ( " Render Async Promise Helpers " ) { [ self ] in
95104 let asyncResolveTypes = skeleton. asyncPromiseResolveReturnTypes
96105 if !asyncResolveTypes. isEmpty {
@@ -875,6 +884,63 @@ public class ExportSwift {
875884 }
876885}
877886
887+ // MARK: - GenericConformanceCodegen
888+
889+ /// Renders `BridgedSwiftGenericBridgeable` conformances for `@JS` types so they
890+ /// can be used as the generic argument of a generic imported `@JSFunction`.
891+ struct GenericConformanceCodegen {
892+ func renderConformance( typeName: String ) -> [ DeclSyntax ] {
893+ let printer = CodeFragmentPrinter ( )
894+ printer. write ( " extension \( typeName) : BridgedSwiftGenericBridgeable { " )
895+ printer. indent {
896+ printer. write (
897+ " @_spi(BridgeJS) public static let bridgeJSTypeHandle = \( typeName) .bridgeJSMakeTypeHandle() "
898+ )
899+ }
900+ printer. write ( " } " )
901+ return [ " \( raw: printer. lines. joined ( separator: " \n " ) ) " ]
902+ }
903+ }
904+
905+ // MARK: - GenericTypeRegistrationCodegen
906+
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+ public struct GenericTypeRegistrationCodegen {
912+ public init ( ) { }
913+
914+ public func render( for skeleton: BridgeJSSkeleton ) -> String ? {
915+ guard let entries = skeleton. typeRegistrationEntries else { return nil }
916+ let abiName = ABINameGenerator . typeRegistrationFunctionName ( moduleName: skeleton. moduleName)
917+ let printer = CodeFragmentPrinter ( )
918+ printer. write ( " #if arch(wasm32) " )
919+ printer. write ( " @_extern(wasm, module: \" bjs \" , name: \" \( abiName) \" ) " )
920+ printer. write ( " fileprivate func _ \( abiName) _extern(_ base: UnsafePointer<Int32>?, _ count: Int32) " )
921+ printer. nextLine ( )
922+ printer. write ( " @_expose(wasm, \" \( abiName) \" ) " )
923+ printer. write ( " public func _ \( abiName) () { " )
924+ printer. indent {
925+ printer. write ( " let typeIds: [Int32] = [ " )
926+ printer. indent {
927+ for entry in entries {
928+ printer. write ( " \( entry. swiftName) .bridgeJSTypeID, " )
929+ }
930+ }
931+ printer. write ( " ] " )
932+ printer. write ( " typeIds.withUnsafeBufferPointer { buffer in " )
933+ printer. indent {
934+ printer. write ( " _ \( abiName) _extern(buffer.baseAddress, Int32(buffer.count)) " )
935+ }
936+ printer. write ( " } " )
937+ }
938+ printer. write ( " } " )
939+ printer. write ( " #endif " )
940+ return printer. lines. joined ( separator: " \n " )
941+ }
942+ }
943+
878944// MARK: - StackCodegen
879945
880946/// Helper for stack-based lifting and lowering operations.
@@ -896,6 +962,10 @@ struct StackCodegen {
896962 return " JSObject.bridgeJSStackPop() "
897963 case . void, . namespaceEnum:
898964 return " () "
965+ case . generic:
966+ fatalError (
967+ " Generic parameters are only supported on imported declarations, not exported concrete-type codegen "
968+ )
899969 }
900970 }
901971
@@ -908,7 +978,7 @@ struct StackCodegen {
908978 return " \( raw: typeName) < \( raw: wrappedType. swiftType) >.bridgeJSStackPop() "
909979 case . jsObject( let className? ) :
910980 return " \( raw: typeName) <JSObject>.bridgeJSStackPop().map { \( raw: className) (unsafelyWrapping: $0) } "
911- case . nullable, . void, . namespaceEnum, . closure, . unsafePointer, . swiftProtocol:
981+ case . nullable, . void, . namespaceEnum, . closure, . unsafePointer, . swiftProtocol, . generic :
912982 fatalError ( " Invalid nullable wrapped type: \( wrappedType) " )
913983 }
914984 }
@@ -941,6 +1011,10 @@ struct StackCodegen {
9411011 return lowerArrayStatements ( elementType: elementType, accessor: accessor, varPrefix: varPrefix)
9421012 case . dictionary( let valueType) :
9431013 return lowerDictionaryStatements ( valueType: valueType, accessor: accessor, varPrefix: varPrefix)
1014+ case . generic:
1015+ fatalError (
1016+ " Generic parameters are only supported on imported declarations, not exported concrete-type codegen "
1017+ )
9441018 }
9451019 }
9461020
@@ -1596,12 +1670,34 @@ extension BridgeType {
15961670 case . associatedValueEnum:
15971671 return [ " _BridgedSwiftAssociatedValueEnum " ]
15981672 case . rawValueEnum, . void, . unsafePointer, . namespaceEnum,
1599- . swiftProtocol, . closure, . nullable, . array, . dictionary, . alias:
1673+ . swiftProtocol, . closure, . nullable, . array, . dictionary, . alias, . generic :
16001674 // Not supported yet.
16011675 return nil
16021676 }
16031677 }
16041678
1679+ /// Stack expressions for bare `T` and `T?`, the only generic shapes that
1680+ /// cannot reuse the concrete emission: `bridgeJSLowerParameter()` names
1681+ /// per-type members that the generic constraint erases to the stack, so
1682+ /// `bridgeJSStackPush()`/`bridgeJSStackPop()` is the shared spelling.
1683+ /// `[T]` and `[String: T]` go through the ordinary paths via the `Array`
1684+ /// and `Dictionary` stack conformances.
1685+ var genericStackPopExpression : String ? {
1686+ switch self {
1687+ case . generic( let name) : return " \( name) .bridgeJSStackPop() "
1688+ case . nullable( . generic( let name) , _) : return " Optional< \( name) >.bridgeJSStackPop() "
1689+ default : return nil
1690+ }
1691+ }
1692+
1693+ func genericStackPushStatement( value: String ) -> String ? {
1694+ switch self {
1695+ case . generic, . nullable( . generic, _) :
1696+ return " \( value) .bridgeJSStackPush() "
1697+ default : return nil
1698+ }
1699+ }
1700+
16051701 var swiftType : String {
16061702 switch self {
16071703 case . bool: return " Bool "
@@ -1631,6 +1727,7 @@ extension BridgeType {
16311727 let closureType = " ( \( paramTypes) ) \( effectsStr) -> \( signature. returnType. swiftType) "
16321728 return useJSTypedClosure ? " JSTypedClosure< \( closureType) > " : closureType
16331729 case . alias( let name, _) : return name
1730+ case . generic( let name) : return name
16341731 }
16351732 }
16361733
@@ -1717,6 +1814,10 @@ extension BridgeType {
17171814 return LiftingIntrinsicInfo ( parameters: [ ] )
17181815 case . alias( _, let underlying) :
17191816 return try underlying. liftParameterInfo ( )
1817+ case . generic:
1818+ throw BridgeJSCoreError (
1819+ " Generic parameters are only supported on imported declarations, not exported concrete-type codegen "
1820+ )
17201821 }
17211822 }
17221823
@@ -1770,6 +1871,10 @@ extension BridgeType {
17701871 return . array
17711872 case . alias( _, let underlying) :
17721873 return try underlying. loweringReturnInfo ( )
1874+ case . generic:
1875+ throw BridgeJSCoreError (
1876+ " Generic parameters are only supported on imported declarations, not exported concrete-type codegen "
1877+ )
17731878 }
17741879 }
17751880}
0 commit comments