@@ -25,6 +25,7 @@ public class ExportSwift {
2525 private var exportedClasses : [ ExportedClass ] = [ ]
2626 private var exportedEnums : [ ExportedEnum ] = [ ]
2727 private var typeDeclResolver : TypeDeclResolver = TypeDeclResolver ( )
28+ private let enumCodegen : EnumCodegen = EnumCodegen ( )
2829
2930 public init ( progress: ProgressReporting , moduleName: String ) {
3031 self . progress = progress
@@ -524,6 +525,31 @@ public class ExportSwift {
524525 )
525526 }
526527
528+ if currentEnum. cases. contains ( where: { !$0. associatedValues. isEmpty } ) {
529+ if case . tsEnum = emitStyle {
530+ diagnose (
531+ node: jsAttribute,
532+ message: " TypeScript enum style is not supported for associated value enums " ,
533+ hint: " Use enumStyle: .const in order to map associated-value enums "
534+ )
535+ }
536+ for enumCase in currentEnum. cases {
537+ for associatedValue in enumCase. associatedValues {
538+ switch associatedValue. type {
539+ case . string, . int, . float, . double, . bool:
540+ break
541+ default :
542+ diagnose (
543+ node: node,
544+ message: " Unsupported associated value type: \( associatedValue. type. swiftType) " ,
545+ hint:
546+ " Only primitive types (String, Int, Float, Double, Bool) are supported in associated-value enums "
547+ )
548+ }
549+ }
550+ }
551+ }
552+
527553 let swiftCallName = ExportSwift . computeSwiftCallName ( for: node, itemName: enumName)
528554 let explicitAccessControl = computeExplicitAtLeastInternalAccessControl (
529555 for: node,
@@ -753,10 +779,15 @@ public class ExportSwift {
753779 decls. append ( Self . prelude)
754780
755781 for enumDef in exportedEnums {
756- if enumDef. enumType == . simple {
757- decls. append ( renderCaseEnumHelpers ( enumDef) )
758- } else {
782+ switch enumDef. enumType {
783+ case . simple:
784+ decls. append ( enumCodegen. renderCaseEnumHelpers ( enumDef) )
785+ case . rawValue:
759786 decls. append ( " extension \( raw: enumDef. swiftCallName) : _BridgedSwiftEnumNoPayload {} " )
787+ case . associatedValue:
788+ decls. append ( enumCodegen. renderAssociatedValueEnumHelpers ( enumDef) )
789+ case . namespace:
790+ ( )
760791 }
761792 }
762793
@@ -770,45 +801,6 @@ public class ExportSwift {
770801 return decls. map { $0. formatted ( using: format) . description } . joined ( separator: " \n \n " )
771802 }
772803
773- func renderCaseEnumHelpers( _ enumDef: ExportedEnum ) -> DeclSyntax {
774- let typeName = enumDef. swiftCallName
775- var initCases : [ String ] = [ ]
776- var valueCases : [ String ] = [ ]
777- for (index, c) in enumDef. cases. enumerated ( ) {
778- initCases. append ( " case \( index) : self = . \( c. name) " )
779- valueCases. append ( " case . \( c. name) : return \( index) " )
780- }
781- let initSwitch = ( [ " switch bridgeJSRawValue { " ] + initCases + [ " default: return nil " , " } " ] ) . joined (
782- separator: " \n "
783- )
784- let valueSwitch = ( [ " switch self { " ] + valueCases + [ " } " ] ) . joined ( separator: " \n " )
785-
786- return """
787- extension \( raw: typeName) {
788- @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
789- return bridgeJSRawValue
790- }
791- @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> \( raw: typeName) {
792- return \( raw: typeName) (bridgeJSRawValue: value)!
793- }
794- @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> \( raw: typeName) {
795- return \( raw: typeName) (bridgeJSRawValue: value)!
796- }
797- @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 {
798- return bridgeJSRawValue
799- }
800-
801- private init?(bridgeJSRawValue: Int32) {
802- \( raw: initSwitch)
803- }
804-
805- private var bridgeJSRawValue: Int32 {
806- \( raw: valueSwitch)
807- }
808- }
809- """
810- }
811-
812804 class ExportedThunkBuilder {
813805 var body : [ CodeBlockItemSyntax ] = [ ]
814806 var liftedParameterExprs : [ ExprSyntax ] = [ ]
@@ -1006,6 +998,159 @@ public class ExportSwift {
1006998 }
1007999 }
10081000
1001+ private struct EnumCodegen {
1002+ func renderCaseEnumHelpers( _ enumDef: ExportedEnum ) -> DeclSyntax {
1003+ let typeName = enumDef. swiftCallName
1004+ var initCases : [ String ] = [ ]
1005+ var valueCases : [ String ] = [ ]
1006+ for (index, enumCase) in enumDef. cases. enumerated ( ) {
1007+ initCases. append ( " case \( index) : self = . \( enumCase. name) " )
1008+ valueCases. append ( " case . \( enumCase. name) : return \( index) " )
1009+ }
1010+ let initSwitch = ( [ " switch bridgeJSRawValue { " ] + initCases + [ " default: return nil " , " } " ] ) . joined (
1011+ separator: " \n "
1012+ )
1013+ let valueSwitch = ( [ " switch self { " ] + valueCases + [ " } " ] ) . joined ( separator: " \n " )
1014+
1015+ return """
1016+ extension \( raw: typeName) {
1017+ @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
1018+ return bridgeJSRawValue
1019+ }
1020+ @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> \( raw: typeName) {
1021+ return \( raw: typeName) (bridgeJSRawValue: value)!
1022+ }
1023+ @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> \( raw: typeName) {
1024+ return \( raw: typeName) (bridgeJSRawValue: value)!
1025+ }
1026+ @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 {
1027+ return bridgeJSRawValue
1028+ }
1029+
1030+ private init?(bridgeJSRawValue: Int32) {
1031+ \( raw: initSwitch)
1032+ }
1033+
1034+ private var bridgeJSRawValue: Int32 {
1035+ \( raw: valueSwitch)
1036+ }
1037+ }
1038+ """
1039+ }
1040+
1041+ func renderAssociatedValueEnumHelpers( _ enumDef: ExportedEnum ) -> DeclSyntax {
1042+ let typeName = enumDef. swiftCallName
1043+ return """
1044+ private extension \( raw: typeName) {
1045+ static func bridgeJSLiftParameter(_ caseId: Int32, _ paramsId: Int32, _ paramsLen: Int32) -> \( raw: typeName) {
1046+ let params: [UInt8] = .init(unsafeUninitializedCapacity: Int(paramsLen)) { buf, initializedCount in
1047+ _swift_js_init_memory(paramsId, buf.baseAddress.unsafelyUnwrapped)
1048+ initializedCount = Int(paramsLen)
1049+ }
1050+ return params.withUnsafeBytes { raw in
1051+ var reader = _BJSBinaryReader(raw: raw)
1052+ switch caseId {
1053+ \( raw: generateBinaryLiftSwitchCases ( enumDef: enumDef) . joined ( separator: " \n " ) )
1054+ default: fatalError( " Unknown \( raw: typeName) case ID: \\ (caseId) " )
1055+ }
1056+ }
1057+ }
1058+
1059+ func bridgeJSLowerReturn() {
1060+ switch self {
1061+ \( raw: generateReturnSwitchCases ( enumDef: enumDef) . joined ( separator: " \n " ) )
1062+ }
1063+ }
1064+ }
1065+ """
1066+ }
1067+
1068+ private func generateBinaryLiftSwitchCases( enumDef: ExportedEnum ) -> [ String ] {
1069+ var cases : [ String ] = [ ]
1070+ for (caseIndex, enumCase) in enumDef. cases. enumerated ( ) {
1071+ if enumCase. associatedValues. isEmpty {
1072+ cases. append ( " case \( caseIndex) : return . \( enumCase. name) " )
1073+ } else {
1074+ var lines : [ String ] = [ ]
1075+ lines. append ( " case \( caseIndex) : " )
1076+ lines. append ( " reader.readParamCount(expected: \( enumCase. associatedValues. count) ) " )
1077+ var argList : [ String ] = [ ]
1078+
1079+ for (paramIndex, associatedValue) in enumCase. associatedValues. enumerated ( ) {
1080+ let paramName = associatedValue. label ?? " param \( paramIndex) "
1081+ argList. append ( paramName)
1082+
1083+ switch associatedValue. type {
1084+ case . string:
1085+ lines. append ( " reader.expectTag(.string) " )
1086+ lines. append ( " let \( paramName) = reader.readString() " )
1087+ case . int:
1088+ lines. append ( " reader.expectTag(.int32) " )
1089+ lines. append ( " let \( paramName) = Int(reader.readInt32()) " )
1090+ case . bool:
1091+ lines. append ( " reader.expectTag(.bool) " )
1092+ lines. append ( " let \( paramName) = Int32(reader.readUInt8()) != 0 " )
1093+ case . float:
1094+ lines. append ( " reader.expectTag(.float32) " )
1095+ lines. append ( " let \( paramName) = reader.readFloat32() " )
1096+ case . double:
1097+ lines. append ( " reader.expectTag(.float64) " )
1098+ lines. append ( " let \( paramName) = reader.readFloat64() " )
1099+ default :
1100+ lines. append ( " reader.expectTag(.int32) " )
1101+ lines. append ( " let \( paramName) = reader.readInt32() " )
1102+ }
1103+ }
1104+
1105+ lines. append ( " return . \( enumCase. name) ( \( argList. joined ( separator: " , " ) ) ) " )
1106+ cases. append ( lines. joined ( separator: " \n " ) )
1107+ }
1108+ }
1109+ return cases
1110+ }
1111+
1112+ private func generateReturnSwitchCases( enumDef: ExportedEnum ) -> [ String ] {
1113+ var cases : [ String ] = [ ]
1114+ for (caseIndex, enumCase) in enumDef. cases. enumerated ( ) {
1115+ if enumCase. associatedValues. isEmpty {
1116+ cases. append ( " case . \( enumCase. name) : " )
1117+ cases. append ( " _swift_js_return_tag(Int32( \( caseIndex) )) " )
1118+ } else {
1119+ var bodyLines : [ String ] = [ ]
1120+ bodyLines. append ( " _swift_js_return_tag(Int32( \( caseIndex) )) " )
1121+ for (index, associatedValue) in enumCase. associatedValues. enumerated ( ) {
1122+ let paramName = associatedValue. label ?? " param \( index) "
1123+ switch associatedValue. type {
1124+ case . string:
1125+ bodyLines. append ( " var __bjs_ \( paramName) = \( paramName) " )
1126+ bodyLines. append ( " __bjs_ \( paramName) .withUTF8 { ptr in " )
1127+ bodyLines. append ( " _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) " )
1128+ bodyLines. append ( " } " )
1129+ case . int:
1130+ bodyLines. append ( " _swift_js_return_int(Int32( \( paramName) )) " )
1131+ case . bool:
1132+ bodyLines. append ( " _swift_js_return_bool( \( paramName) ? 1 : 0) " )
1133+ case . float:
1134+ bodyLines. append ( " _swift_js_return_f32( \( paramName) ) " )
1135+ case . double:
1136+ bodyLines. append ( " _swift_js_return_f64( \( paramName) ) " )
1137+ default :
1138+ bodyLines. append (
1139+ " preconditionFailure( \" BridgeJS: unsupported associated value type in generated code \" ) "
1140+ )
1141+ }
1142+ }
1143+ let pattern = enumCase. associatedValues. enumerated ( )
1144+ . map { index, associatedValue in " let \( associatedValue. label ?? " param \( index) " ) " }
1145+ . joined ( separator: " , " )
1146+ cases. append ( " case . \( enumCase. name) ( \( pattern) ): " )
1147+ cases. append ( contentsOf: bodyLines)
1148+ }
1149+ }
1150+ return cases
1151+ }
1152+ }
1153+
10091154 func renderSingleExportedFunction( function: ExportedFunction ) throws -> DeclSyntax {
10101155 let builder = ExportedThunkBuilder ( effects: function. effects)
10111156 for param in function. parameters {
@@ -1264,6 +1409,9 @@ extension BridgeType {
12641409 static let swiftHeapObject = LiftingIntrinsicInfo ( parameters: [ ( " value " , . pointer) ] )
12651410 static let void = LiftingIntrinsicInfo ( parameters: [ ] )
12661411 static let caseEnum = LiftingIntrinsicInfo ( parameters: [ ( " value " , . i32) ] )
1412+ static let associatedValueEnum = LiftingIntrinsicInfo ( parameters: [
1413+ ( " caseId " , . i32) , ( " paramsId " , . i32) , ( " paramsLen " , . i32) ,
1414+ ] )
12671415 }
12681416
12691417 func liftParameterInfo( ) throws -> LiftingIntrinsicInfo {
@@ -1291,7 +1439,7 @@ extension BridgeType {
12911439 case . uint64: return . int
12921440 }
12931441 case . associatedValueEnum:
1294- throw BridgeJSCoreError ( " Associated value enums are not supported to pass as parameters " )
1442+ return . associatedValueEnum
12951443 case . namespaceEnum:
12961444 throw BridgeJSCoreError ( " Namespace enums are not supported to pass as parameters " )
12971445 }
@@ -1310,6 +1458,7 @@ extension BridgeType {
13101458 static let void = LoweringIntrinsicInfo ( returnType: nil )
13111459 static let caseEnum = LoweringIntrinsicInfo ( returnType: . i32)
13121460 static let rawValueEnum = LoweringIntrinsicInfo ( returnType: . i32)
1461+ static let associatedValueEnum = LoweringIntrinsicInfo ( returnType: nil )
13131462 }
13141463
13151464 func loweringReturnInfo( ) throws -> LoweringIntrinsicInfo {
@@ -1337,7 +1486,7 @@ extension BridgeType {
13371486 case . uint64: return . int
13381487 }
13391488 case . associatedValueEnum:
1340- throw BridgeJSCoreError ( " Associated value enums are not supported to pass as parameters " )
1489+ return . associatedValueEnum
13411490 case . namespaceEnum:
13421491 throw BridgeJSCoreError ( " Namespace enums are not supported to pass as parameters " )
13431492 }
0 commit comments