@@ -167,12 +167,7 @@ public class ExportSwift {
167167 private func protocolCastSuffix ( for returnType : BridgeType ) -> ( prefix : String , suffix : String ) {
168168 switch returnType {
169169 case . swiftProtocol :
170- return ( " " , " as! \( returnType . swiftType ) " )
171- case . nullable ( let wrappedType , _) :
172- if case . swiftProtocol = wrappedType {
173- return ( " ( " , " ).flatMap { $0 as? \( wrappedType . swiftType ) } " )
174- }
175- return ( " " , " " )
170+ return ( " " , " as! _BridgedSwiftProtocolExportable " )
176171 default :
177172 return ( " " , " " )
178173 }
@@ -311,6 +306,23 @@ public class ExportSwift {
311306 for stmt in stackCodegen . lowerStatements ( for : returnType , accessor : " ret " , varPrefix : " ret " ) {
312307 append ( stmt )
313308 }
309+ case . dictionary ( . swiftProtocol ) :
310+ let stackCodegen = StackCodegen ( )
311+ for stmt in stackCodegen . lowerStatements ( for : returnType , accessor : " ret " , varPrefix : " ret " ) {
312+ append ( stmt )
313+ }
314+ case . swiftProtocol :
315+ append ( " return ret._bridgeJSLowerAsProtocolReturn() " )
316+ case . nullable ( . swiftProtocol , _) :
317+ append (
318+ """
319+ if let ret {
320+ _swift_js_return_optional_object(1, (ret as! _BridgedSwiftProtocolExportable)._bridgeJSLowerAsProtocolReturn())
321+ } else {
322+ _swift_js_return_optional_object(0, 0)
323+ }
324+ """
325+ )
314326 default :
315327 append ( " return ret.bridgeJSLowerReturn() " )
316328 }
@@ -702,11 +714,17 @@ public class ExportSwift {
702714
703715 // If the class has an explicit access control, we need to add it to the extension declaration.
704716 let accessControl = klass . explicitAccessControl . map { " \( $0 ) " } ?? " "
717+ // @_spi can only be applied to public/open declarations
718+ let isPublicOrOpen = klass . explicitAccessControl == " public " || klass . explicitAccessControl == " open "
719+ let spiPrefix = isPublicOrOpen ? " @_spi(BridgeJS) " : " "
705720 let extensionDecl : DeclSyntax = """
706- extension \( raw : klass . swiftCallName ) : ConvertibleToJSValue, _BridgedSwiftHeapObject {
721+ extension \( raw : klass . swiftCallName ) : ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
707722 \( raw : accessControl ) var jsValue: JSValue {
708723 return .object(JSObject(id: UInt32(bitPattern: \( raw : wrapFunctionName ) (Unmanaged.passRetained(self).toOpaque()))))
709724 }
725+ \( raw : spiPrefix ) \( raw : accessControl ) consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
726+ \( raw : wrapFunctionName ) (Unmanaged.passRetained(self).toOpaque())
727+ }
710728 }
711729 """
712730 // Build common function signature
@@ -781,7 +799,9 @@ struct StackCodegen {
781799 case . jsObject ( _? ) :
782800 return [ " \( raw : accessor ) .jsObject.bridgeJSStackPush() " ]
783801 case . swiftProtocol :
784- return [ " ( \( raw : accessor ) as! \( raw : type . swiftType ) ).bridgeJSStackPush() " ]
802+ return [
803+ " _swift_js_push_i32(( \( raw : accessor ) as! _BridgedSwiftProtocolExportable)._bridgeJSLowerAsProtocolReturn()) "
804+ ]
785805 case . void , . namespaceEnum :
786806 return [ ]
787807 case . array ( let elementType ) :
@@ -798,14 +818,29 @@ struct StackCodegen {
798818 ) -> [ CodeBlockItemSyntax ] {
799819 switch elementType {
800820 case . swiftProtocol :
801- return [ " \( raw : accessor ) .map { $0 as! \( raw : elementType . swiftType ) }.bridgeJSStackPush() " ]
821+ return lowerProtocolArrayStatements ( accessor : accessor , varPrefix : varPrefix )
802822 case . void , . namespaceEnum :
803823 fatalError ( " Invalid array element type: \( elementType ) " )
804824 default :
805825 return [ " \( raw : accessor ) .bridgeJSStackPush() " ]
806826 }
807827 }
808828
829+ private func lowerProtocolArrayStatements (
830+ accessor : String ,
831+ varPrefix : String
832+ ) -> [ CodeBlockItemSyntax ] {
833+ var statements : [ CodeBlockItemSyntax ] = [ ]
834+ let elemVar = " __bjs_elem_ \( varPrefix ) "
835+ statements . append ( " for \( raw : elemVar ) in \( raw : accessor ) { " )
836+ statements . append (
837+ " _swift_js_push_i32(( \( raw : elemVar ) as! _BridgedSwiftProtocolExportable)._bridgeJSLowerAsProtocolReturn()) "
838+ )
839+ statements . append ( " } " )
840+ statements . append ( " _swift_js_push_i32(Int32( \( raw : accessor ) .count)) " )
841+ return statements
842+ }
843+
809844 private func lowerDictionaryStatements (
810845 valueType : BridgeType ,
811846 accessor : String ,
@@ -815,7 +850,7 @@ struct StackCodegen {
815850 case . jsObject ( let className ? ) where className != " JSObject " :
816851 return [ " \( raw : accessor ) .mapValues { $0.jsObject }.bridgeJSStackPush() " ]
817852 case . swiftProtocol :
818- return [ " \( raw : accessor ) .mapValues { $0 as! \( raw : valueType . swiftType ) }.bridgeJSStackPush() " ]
853+ return lowerProtocolDictionaryStatements ( accessor : accessor , varPrefix : varPrefix )
819854 case . nullable , . closure :
820855 return lowerDictionaryStatementsInline (
821856 valueType : valueType ,
@@ -862,6 +897,22 @@ struct StackCodegen {
862897 statements . append ( " _swift_js_push_i32(Int32( \( raw : accessor ) .count)) " )
863898 return statements
864899 }
900+
901+ private func lowerProtocolDictionaryStatements (
902+ accessor : String ,
903+ varPrefix : String
904+ ) -> [ CodeBlockItemSyntax ] {
905+ var statements : [ CodeBlockItemSyntax ] = [ ]
906+ let pairVar = " __bjs_kv_ \( varPrefix ) "
907+ statements . append ( " for \( raw : pairVar ) in \( raw : accessor ) { " )
908+ statements . append ( " \( raw : pairVar ) .key.bridgeJSStackPush() " )
909+ statements . append (
910+ " _swift_js_push_i32(( \( raw : pairVar ) .value as! _BridgedSwiftProtocolExportable)._bridgeJSLowerAsProtocolReturn()) "
911+ )
912+ statements . append ( " } " )
913+ statements . append ( " _swift_js_push_i32(Int32( \( raw : accessor ) .count)) " )
914+ return statements
915+ }
865916}
866917
867918// MARK: - EnumCodegen
0 commit comments