Skip to content

Commit 1cc4ced

Browse files
committed
BridgeJS: Fix protocol existential lowering in Swift-to-JS direction
1 parent 7dd0263 commit 1cc4ced

34 files changed

+743
-108
lines changed

Benchmarks/Sources/Generated/BridgeJS.swift

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,13 @@ public func _bjs_EnumRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> Voi
491491
#endif
492492
}
493493

494-
extension EnumRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
494+
extension EnumRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
495495
var jsValue: JSValue {
496496
return .object(JSObject(id: UInt32(bitPattern: _bjs_EnumRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
497497
}
498+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
499+
_bjs_EnumRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
500+
}
498501
}
499502

500503
#if arch(wasm32)
@@ -628,10 +631,13 @@ public func _bjs_ComplexResultRoundtrip_deinit(_ pointer: UnsafeMutableRawPointe
628631
#endif
629632
}
630633

631-
extension ComplexResultRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
634+
extension ComplexResultRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
632635
var jsValue: JSValue {
633636
return .object(JSObject(id: UInt32(bitPattern: _bjs_ComplexResultRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
634637
}
638+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
639+
_bjs_ComplexResultRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
640+
}
635641
}
636642

637643
#if arch(wasm32)
@@ -688,10 +694,13 @@ public func _bjs_StringRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> V
688694
#endif
689695
}
690696

691-
extension StringRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
697+
extension StringRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
692698
var jsValue: JSValue {
693699
return .object(JSObject(id: UInt32(bitPattern: _bjs_StringRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
694700
}
701+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
702+
_bjs_StringRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
703+
}
695704
}
696705

697706
#if arch(wasm32)
@@ -815,10 +824,13 @@ public func _bjs_OptionalReturnRoundtrip_deinit(_ pointer: UnsafeMutableRawPoint
815824
#endif
816825
}
817826

818-
extension OptionalReturnRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
827+
extension OptionalReturnRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
819828
var jsValue: JSValue {
820829
return .object(JSObject(id: UInt32(bitPattern: _bjs_OptionalReturnRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
821830
}
831+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
832+
_bjs_OptionalReturnRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
833+
}
822834
}
823835

824836
#if arch(wasm32)
@@ -982,10 +994,13 @@ public func _bjs_StructRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> V
982994
#endif
983995
}
984996

985-
extension StructRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
997+
extension StructRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
986998
var jsValue: JSValue {
987999
return .object(JSObject(id: UInt32(bitPattern: _bjs_StructRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
9881000
}
1001+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
1002+
_bjs_StructRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
1003+
}
9891004
}
9901005

9911006
#if arch(wasm32)
@@ -1126,10 +1141,13 @@ public func _bjs_SimpleClass_deinit(_ pointer: UnsafeMutableRawPointer) -> Void
11261141
#endif
11271142
}
11281143

1129-
extension SimpleClass: ConvertibleToJSValue, _BridgedSwiftHeapObject {
1144+
extension SimpleClass: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
11301145
var jsValue: JSValue {
11311146
return .object(JSObject(id: UInt32(bitPattern: _bjs_SimpleClass_wrap(Unmanaged.passRetained(self).toOpaque()))))
11321147
}
1148+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
1149+
_bjs_SimpleClass_wrap(Unmanaged.passRetained(self).toOpaque())
1150+
}
11331151
}
11341152

11351153
#if arch(wasm32)
@@ -1228,10 +1246,13 @@ public func _bjs_AddressClass_deinit(_ pointer: UnsafeMutableRawPointer) -> Void
12281246
#endif
12291247
}
12301248

1231-
extension AddressClass: ConvertibleToJSValue, _BridgedSwiftHeapObject {
1249+
extension AddressClass: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
12321250
var jsValue: JSValue {
12331251
return .object(JSObject(id: UInt32(bitPattern: _bjs_AddressClass_wrap(Unmanaged.passRetained(self).toOpaque()))))
12341252
}
1253+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
1254+
_bjs_AddressClass_wrap(Unmanaged.passRetained(self).toOpaque())
1255+
}
12351256
}
12361257

12371258
#if arch(wasm32)
@@ -1331,10 +1352,13 @@ public func _bjs_ClassRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> Vo
13311352
#endif
13321353
}
13331354

1334-
extension ClassRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
1355+
extension ClassRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
13351356
var jsValue: JSValue {
13361357
return .object(JSObject(id: UInt32(bitPattern: _bjs_ClassRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
13371358
}
1359+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
1360+
_bjs_ClassRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
1361+
}
13381362
}
13391363

13401364
#if arch(wasm32)
@@ -1691,10 +1715,13 @@ public func _bjs_ArrayRoundtrip_deinit(_ pointer: UnsafeMutableRawPointer) -> Vo
16911715
#endif
16921716
}
16931717

1694-
extension ArrayRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject {
1718+
extension ArrayRoundtrip: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
16951719
var jsValue: JSValue {
16961720
return .object(JSObject(id: UInt32(bitPattern: _bjs_ArrayRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque()))))
16971721
}
1722+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
1723+
_bjs_ArrayRoundtrip_wrap(Unmanaged.passRetained(self).toOpaque())
1724+
}
16981725
}
16991726

17001727
#if arch(wasm32)

Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,13 @@ public func _bjs_PlayBridgeJS_deinit(_ pointer: UnsafeMutableRawPointer) -> Void
209209
#endif
210210
}
211211

212-
extension PlayBridgeJS: ConvertibleToJSValue, _BridgedSwiftHeapObject {
212+
extension PlayBridgeJS: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
213213
var jsValue: JSValue {
214214
return .object(JSObject(id: UInt32(bitPattern: _bjs_PlayBridgeJS_wrap(Unmanaged.passRetained(self).toOpaque()))))
215215
}
216+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
217+
_bjs_PlayBridgeJS_wrap(Unmanaged.passRetained(self).toOpaque())
218+
}
216219
}
217220

218221
#if arch(wasm32)

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,10 +414,13 @@ public func _bjs_Item_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
414414
#endif
415415
}
416416

417-
extension Item: ConvertibleToJSValue, _BridgedSwiftHeapObject {
417+
extension Item: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
418418
var jsValue: JSValue {
419419
return .object(JSObject(id: UInt32(bitPattern: _bjs_Item_wrap(Unmanaged.passRetained(self).toOpaque()))))
420420
}
421+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
422+
_bjs_Item_wrap(Unmanaged.passRetained(self).toOpaque())
423+
}
421424
}
422425

423426
#if arch(wasm32)
@@ -477,10 +480,13 @@ public func _bjs_MultiArrayContainer_deinit(_ pointer: UnsafeMutableRawPointer)
477480
#endif
478481
}
479482

480-
extension MultiArrayContainer: ConvertibleToJSValue, _BridgedSwiftHeapObject {
483+
extension MultiArrayContainer: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
481484
var jsValue: JSValue {
482485
return .object(JSObject(id: UInt32(bitPattern: _bjs_MultiArrayContainer_wrap(Unmanaged.passRetained(self).toOpaque()))))
483486
}
487+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
488+
_bjs_MultiArrayContainer_wrap(Unmanaged.passRetained(self).toOpaque())
489+
}
484490
}
485491

486492
#if arch(wasm32)

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.ReverseOrder.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@ public func _bjs_FunctionA_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
5252
#endif
5353
}
5454

55-
extension FunctionA: ConvertibleToJSValue, _BridgedSwiftHeapObject {
55+
extension FunctionA: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
5656
var jsValue: JSValue {
5757
return .object(JSObject(id: UInt32(bitPattern: _bjs_FunctionA_wrap(Unmanaged.passRetained(self).toOpaque()))))
5858
}
59+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
60+
_bjs_FunctionA_wrap(Unmanaged.passRetained(self).toOpaque())
61+
}
5962
}
6063

6164
#if arch(wasm32)
@@ -112,10 +115,13 @@ public func _bjs_FunctionB_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
112115
#endif
113116
}
114117

115-
extension FunctionB: ConvertibleToJSValue, _BridgedSwiftHeapObject {
118+
extension FunctionB: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
116119
var jsValue: JSValue {
117120
return .object(JSObject(id: UInt32(bitPattern: _bjs_FunctionB_wrap(Unmanaged.passRetained(self).toOpaque()))))
118121
}
122+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
123+
_bjs_FunctionB_wrap(Unmanaged.passRetained(self).toOpaque())
124+
}
119125
}
120126

121127
#if arch(wasm32)

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileFunctionTypes.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ public func _bjs_FunctionB_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
5151
#endif
5252
}
5353

54-
extension FunctionB: ConvertibleToJSValue, _BridgedSwiftHeapObject {
54+
extension FunctionB: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
5555
var jsValue: JSValue {
5656
return .object(JSObject(id: UInt32(bitPattern: _bjs_FunctionB_wrap(Unmanaged.passRetained(self).toOpaque()))))
5757
}
58+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
59+
_bjs_FunctionB_wrap(Unmanaged.passRetained(self).toOpaque())
60+
}
5861
}
5962

6063
#if arch(wasm32)
@@ -112,10 +115,13 @@ public func _bjs_FunctionA_deinit(_ pointer: UnsafeMutableRawPointer) -> Void {
112115
#endif
113116
}
114117

115-
extension FunctionA: ConvertibleToJSValue, _BridgedSwiftHeapObject {
118+
extension FunctionA: ConvertibleToJSValue, _BridgedSwiftHeapObject, _BridgedSwiftProtocolExportable {
116119
var jsValue: JSValue {
117120
return .object(JSObject(id: UInt32(bitPattern: _bjs_FunctionA_wrap(Unmanaged.passRetained(self).toOpaque()))))
118121
}
122+
consuming func _bridgeJSLowerAsProtocolReturn() -> Int32 {
123+
_bjs_FunctionA_wrap(Unmanaged.passRetained(self).toOpaque())
124+
}
119125
}
120126

121127
#if arch(wasm32)

0 commit comments

Comments
 (0)