Skip to content

Commit c814901

Browse files
committed
BridgeJS: Enforce throws(JSException) on @js protocol methods
1 parent 4fa48cf commit c814901

File tree

7 files changed

+72
-63
lines changed

7 files changed

+72
-63
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ struct ProtocolCodegen {
14231423
let signature = SwiftSignatureBuilder.buildFunctionSignature(
14241424
parameters: method.parameters,
14251425
returnType: method.returnType,
1426-
effects: nil
1426+
effects: method.effects
14271427
)
14281428

14291429
// Build extern declaration using helper function

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,15 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
16081608
return nil
16091609
}
16101610

1611+
guard effects.isThrows else {
1612+
diagnose(
1613+
node: node,
1614+
message: "@JS protocol methods must be throws.",
1615+
hint: "Declare the method as 'throws(JSException)'."
1616+
)
1617+
return nil
1618+
}
1619+
16111620
return ExportedFunction(
16121621
name: name,
16131622
abiName: abiName,

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Protocol.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,18 @@ import JavaScriptKit
4747
var directionOptional: Direction? { get set }
4848
var priority: Priority { get set }
4949
var priorityOptional: Priority? { get set }
50-
func onSomethingHappened()
51-
func onValueChanged(_ value: String)
52-
func onCountUpdated(count: Int) -> Bool
53-
func onLabelUpdated(_ prefix: String, _ suffix: String)
54-
func isCountEven() -> Bool
55-
func onHelperUpdated(_ helper: Helper)
56-
func createHelper() -> Helper
57-
func onOptionalHelperUpdated(_ helper: Helper?)
58-
func createOptionalHelper() -> Helper?
59-
func createEnum() -> ExampleEnum
60-
func handleResult(_ result: Result)
61-
func getResult() -> Result
50+
func onSomethingHappened() throws(JSException)
51+
func onValueChanged(_ value: String) throws(JSException)
52+
func onCountUpdated(count: Int) throws(JSException) -> Bool
53+
func onLabelUpdated(_ prefix: String, _ suffix: String) throws(JSException)
54+
func isCountEven() throws(JSException) -> Bool
55+
func onHelperUpdated(_ helper: Helper) throws(JSException)
56+
func createHelper() throws(JSException) -> Helper
57+
func onOptionalHelperUpdated(_ helper: Helper?) throws(JSException)
58+
func createOptionalHelper() throws(JSException) -> Helper?
59+
func createEnum() throws(JSException) -> ExampleEnum
60+
func handleResult(_ result: Result) throws(JSException)
61+
func getResult() throws(JSException) -> Result
6262
}
6363

6464
@JS class MyViewController {

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Protocol.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@
498498
"effects" : {
499499
"isAsync" : false,
500500
"isStatic" : false,
501-
"isThrows" : false
501+
"isThrows" : true
502502
},
503503
"name" : "onSomethingHappened",
504504
"parameters" : [
@@ -515,7 +515,7 @@
515515
"effects" : {
516516
"isAsync" : false,
517517
"isStatic" : false,
518-
"isThrows" : false
518+
"isThrows" : true
519519
},
520520
"name" : "onValueChanged",
521521
"parameters" : [
@@ -540,7 +540,7 @@
540540
"effects" : {
541541
"isAsync" : false,
542542
"isStatic" : false,
543-
"isThrows" : false
543+
"isThrows" : true
544544
},
545545
"name" : "onCountUpdated",
546546
"parameters" : [
@@ -565,7 +565,7 @@
565565
"effects" : {
566566
"isAsync" : false,
567567
"isStatic" : false,
568-
"isThrows" : false
568+
"isThrows" : true
569569
},
570570
"name" : "onLabelUpdated",
571571
"parameters" : [
@@ -599,7 +599,7 @@
599599
"effects" : {
600600
"isAsync" : false,
601601
"isStatic" : false,
602-
"isThrows" : false
602+
"isThrows" : true
603603
},
604604
"name" : "isCountEven",
605605
"parameters" : [
@@ -616,7 +616,7 @@
616616
"effects" : {
617617
"isAsync" : false,
618618
"isStatic" : false,
619-
"isThrows" : false
619+
"isThrows" : true
620620
},
621621
"name" : "onHelperUpdated",
622622
"parameters" : [
@@ -641,7 +641,7 @@
641641
"effects" : {
642642
"isAsync" : false,
643643
"isStatic" : false,
644-
"isThrows" : false
644+
"isThrows" : true
645645
},
646646
"name" : "createHelper",
647647
"parameters" : [
@@ -658,7 +658,7 @@
658658
"effects" : {
659659
"isAsync" : false,
660660
"isStatic" : false,
661-
"isThrows" : false
661+
"isThrows" : true
662662
},
663663
"name" : "onOptionalHelperUpdated",
664664
"parameters" : [
@@ -687,7 +687,7 @@
687687
"effects" : {
688688
"isAsync" : false,
689689
"isStatic" : false,
690-
"isThrows" : false
690+
"isThrows" : true
691691
},
692692
"name" : "createOptionalHelper",
693693
"parameters" : [
@@ -708,7 +708,7 @@
708708
"effects" : {
709709
"isAsync" : false,
710710
"isStatic" : false,
711-
"isThrows" : false
711+
"isThrows" : true
712712
},
713713
"name" : "createEnum",
714714
"parameters" : [
@@ -726,7 +726,7 @@
726726
"effects" : {
727727
"isAsync" : false,
728728
"isStatic" : false,
729-
"isThrows" : false
729+
"isThrows" : true
730730
},
731731
"name" : "handleResult",
732732
"parameters" : [
@@ -751,7 +751,7 @@
751751
"effects" : {
752752
"isAsync" : false,
753753
"isStatic" : false,
754-
"isThrows" : false
754+
"isThrows" : true
755755
},
756756
"name" : "getResult",
757757
"parameters" : [

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Protocol.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,74 @@
11
struct AnyMyViewControllerDelegate: MyViewControllerDelegate, _BridgedSwiftProtocolWrapper {
22
let jsObject: JSObject
33

4-
func onSomethingHappened() -> Void {
4+
func onSomethingHappened() throws(JSException) -> Void {
55
let jsObjectValue = jsObject.bridgeJSLowerParameter()
66
_extern_onSomethingHappened(jsObjectValue)
77
}
88

9-
func onValueChanged(_ value: String) -> Void {
9+
func onValueChanged(_ value: String) throws(JSException) -> Void {
1010
let jsObjectValue = jsObject.bridgeJSLowerParameter()
1111
let valueValue = value.bridgeJSLowerParameter()
1212
_extern_onValueChanged(jsObjectValue, valueValue)
1313
}
1414

15-
func onCountUpdated(count: Int) -> Bool {
15+
func onCountUpdated(count: Int) throws(JSException) -> Bool {
1616
let jsObjectValue = jsObject.bridgeJSLowerParameter()
1717
let countValue = count.bridgeJSLowerParameter()
1818
let ret = _extern_onCountUpdated(jsObjectValue, countValue)
1919
return Bool.bridgeJSLiftReturn(ret)
2020
}
2121

22-
func onLabelUpdated(_ prefix: String, _ suffix: String) -> Void {
22+
func onLabelUpdated(_ prefix: String, _ suffix: String) throws(JSException) -> Void {
2323
let jsObjectValue = jsObject.bridgeJSLowerParameter()
2424
let prefixValue = prefix.bridgeJSLowerParameter()
2525
let suffixValue = suffix.bridgeJSLowerParameter()
2626
_extern_onLabelUpdated(jsObjectValue, prefixValue, suffixValue)
2727
}
2828

29-
func isCountEven() -> Bool {
29+
func isCountEven() throws(JSException) -> Bool {
3030
let jsObjectValue = jsObject.bridgeJSLowerParameter()
3131
let ret = _extern_isCountEven(jsObjectValue)
3232
return Bool.bridgeJSLiftReturn(ret)
3333
}
3434

35-
func onHelperUpdated(_ helper: Helper) -> Void {
35+
func onHelperUpdated(_ helper: Helper) throws(JSException) -> Void {
3636
let jsObjectValue = jsObject.bridgeJSLowerParameter()
3737
let helperPointer = helper.bridgeJSLowerParameter()
3838
_extern_onHelperUpdated(jsObjectValue, helperPointer)
3939
}
4040

41-
func createHelper() -> Helper {
41+
func createHelper() throws(JSException) -> Helper {
4242
let jsObjectValue = jsObject.bridgeJSLowerParameter()
4343
let ret = _extern_createHelper(jsObjectValue)
4444
return Helper.bridgeJSLiftReturn(ret)
4545
}
4646

47-
func onOptionalHelperUpdated(_ helper: Optional<Helper>) -> Void {
47+
func onOptionalHelperUpdated(_ helper: Optional<Helper>) throws(JSException) -> Void {
4848
let jsObjectValue = jsObject.bridgeJSLowerParameter()
4949
let (helperIsSome, helperPointer) = helper.bridgeJSLowerParameter()
5050
_extern_onOptionalHelperUpdated(jsObjectValue, helperIsSome, helperPointer)
5151
}
5252

53-
func createOptionalHelper() -> Optional<Helper> {
53+
func createOptionalHelper() throws(JSException) -> Optional<Helper> {
5454
let jsObjectValue = jsObject.bridgeJSLowerParameter()
5555
let ret = _extern_createOptionalHelper(jsObjectValue)
5656
return Optional<Helper>.bridgeJSLiftReturn(ret)
5757
}
5858

59-
func createEnum() -> ExampleEnum {
59+
func createEnum() throws(JSException) -> ExampleEnum {
6060
let jsObjectValue = jsObject.bridgeJSLowerParameter()
6161
let ret = _extern_createEnum(jsObjectValue)
6262
return ExampleEnum.bridgeJSLiftReturn(ret)
6363
}
6464

65-
func handleResult(_ result: Result) -> Void {
65+
func handleResult(_ result: Result) throws(JSException) -> Void {
6666
let jsObjectValue = jsObject.bridgeJSLowerParameter()
6767
let resultCaseId = result.bridgeJSLowerParameter()
6868
_extern_handleResult(jsObjectValue, resultCaseId)
6969
}
7070

71-
func getResult() -> Result {
71+
func getResult() throws(JSException) -> Result {
7272
let jsObjectValue = jsObject.bridgeJSLowerParameter()
7373
let ret = _extern_getResult(jsObjectValue)
7474
return Result.bridgeJSLiftReturn(ret)

Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Protocols.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ When you mark a protocol with `@JS`, BridgeJS generates:
1515

1616
## Example: Counter Protocol
1717

18-
Mark a Swift protocol with `@JS` to expose it:
18+
Mark a Swift protocol with `@JS` to expose it. Protocol methods must declare `throws(JSException)` to align with the import side error handling:
1919

2020
```swift
2121
import JavaScriptKit
@@ -24,9 +24,9 @@ import JavaScriptKit
2424
var count: Int { get set }
2525
var name: String { get }
2626
var label: String? { get set }
27-
func increment(by amount: Int)
28-
func reset()
29-
func getValue() -> Int
27+
func increment(by amount: Int) throws(JSException)
28+
func reset() throws(JSException)
29+
func getValue() throws(JSException) -> Int
3030
}
3131

3232
@JS class CounterManager {
@@ -122,24 +122,24 @@ You can also implement protocols in Swift and use them from JavaScript:
122122
@JS protocol Counter {
123123
var count: Int { get set }
124124
var name: String { get }
125-
func increment(by amount: Int)
126-
func reset()
127-
func getValue() -> Int
125+
func increment(by amount: Int) throws(JSException)
126+
func reset() throws(JSException)
127+
func getValue() throws(JSException) -> Int
128128
}
129129

130130
final class SwiftCounter: Counter {
131131
var count = 0
132132
let name = "SwiftCounter"
133-
134-
func increment(by amount: Int) {
133+
134+
func increment(by amount: Int) throws(JSException) {
135135
count += amount
136136
}
137-
138-
func reset() {
137+
138+
func reset() throws(JSException) {
139139
count = 0
140140
}
141-
142-
func getValue() -> Int {
141+
142+
func getValue() throws(JSException) -> Int {
143143
return count
144144
}
145145
}
@@ -195,7 +195,7 @@ struct AnyCounter: Counter, _BridgedSwiftProtocolWrapper {
195195
}
196196
}
197197

198-
func increment(by amount: Int) {
198+
func increment(by amount: Int) throws(JSException) {
199199
@_extern(wasm, module: "TestModule", name: "bjs_Counter_increment")
200200
func _extern_increment(this: Int32, amount: Int32)
201201
_extern_increment(
@@ -212,7 +212,7 @@ struct AnyCounter: Counter, _BridgedSwiftProtocolWrapper {
212212

213213
| Swift Feature | Status |
214214
|:--------------|:-------|
215-
| Method requirements: `func foo(_ param: String?) -> FooClass?` ||
215+
| Method requirements: `func foo(_ param: String?) throws(JSException) -> FooClass?` ||
216216
| Property requirements: `var property: Type { get }` / `var property: Type { get set }` ||
217217
| Optional parameters / return values in methods ||
218218
| Optional properties ||

Tests/BridgeJSRuntimeTests/ExportAPITests.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -988,17 +988,17 @@ enum GraphOperations {
988988
var apiResult: APIResult? { get set }
989989
var helper: Greeter { get set }
990990
var optionalHelper: Greeter? { get set }
991-
func increment(by amount: Int)
992-
func getValue() -> Int
993-
func setLabelElements(_ labelPrefix: String, _ labelSuffix: String)
994-
func getLabel() -> String
995-
func isEven() -> Bool
996-
func processGreeter(_ greeter: Greeter) -> String
997-
func createGreeter() -> Greeter
998-
func processOptionalGreeter(_ greeter: Greeter?) -> String
999-
func createOptionalGreeter() -> Greeter?
1000-
func handleAPIResult(_ result: APIResult?)
1001-
func getAPIResult() -> APIResult?
991+
func increment(by amount: Int) throws(JSException)
992+
func getValue() throws(JSException) -> Int
993+
func setLabelElements(_ labelPrefix: String, _ labelSuffix: String) throws(JSException)
994+
func getLabel() throws(JSException) -> String
995+
func isEven() throws(JSException) -> Bool
996+
func processGreeter(_ greeter: Greeter) throws(JSException) -> String
997+
func createGreeter() throws(JSException) -> Greeter
998+
func processOptionalGreeter(_ greeter: Greeter?) throws(JSException) -> String
999+
func createOptionalGreeter() throws(JSException) -> Greeter?
1000+
func handleAPIResult(_ result: APIResult?) throws(JSException)
1001+
func getAPIResult() throws(JSException) -> APIResult?
10021002
}
10031003

10041004
@JS class DataProcessorManager {

0 commit comments

Comments
 (0)