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