Skip to content

Commit 94388fa

Browse files
authored
Merge pull request #820 from wfltaylor/async-throws-protocol
BridgeJS: Support exporting protocol methods with async and throws
2 parents 67f3be5 + 681bb0b commit 94388fa

20 files changed

Lines changed: 1656 additions & 41 deletions

File tree

‎Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ struct ProtocolCodegen {
14391439
let signature = SwiftSignatureBuilder.buildFunctionSignature(
14401440
parameters: method.parameters,
14411441
returnType: method.returnType,
1442-
effects: nil
1442+
effects: method.effects
14431443
)
14441444

14451445
// Build extern declaration using helper function

‎Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift‎

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2897,9 +2897,8 @@ extension BridgeJSLink {
28972897

28982898
/// Renders an async import function with resolve/reject closure refs.
28992899
///
2900-
/// The generated function takes `resolveRef` and `rejectRef` as the first parameters,
2901-
/// looks up the resolve/reject closures from memory, and executes the body which
2902-
/// chains `.then(resolve, reject)` on the import's returned Promise.
2900+
/// The generated function takes `resolveRef` and `rejectRef` as the first parameters
2901+
/// and settles them with the result of the import call.
29032902
private func renderAsyncFunction(name: String?) -> [String] {
29042903
let printer = CodeFragmentPrinter()
29052904
let allParams = ["resolveRef", "rejectRef"] + parameterNames
@@ -2908,7 +2907,15 @@ extension BridgeJSLink {
29082907
let s = JSGlueVariableScope.reservedSwift
29092908
printer.write("const resolve = \(s).memory.getObject(resolveRef);")
29102909
printer.write("const reject = \(s).memory.getObject(rejectRef);")
2911-
printer.write(contentsOf: body)
2910+
printer.write("try {")
2911+
printer.indent {
2912+
printer.write(contentsOf: body)
2913+
}
2914+
printer.write("} catch (error) {")
2915+
printer.indent {
2916+
printer.write("reject(error);")
2917+
}
2918+
printer.write("}")
29122919
}
29132920
printer.write("}")
29142921
return printer.lines
@@ -2930,7 +2937,7 @@ extension BridgeJSLink {
29302937

29312938
private func call(callExpr: String) throws {
29322939
if effects.isAsync {
2933-
body.write("\(callExpr).then(resolve, reject);")
2940+
body.write("Promise.resolve(\(callExpr)).then(resolve, reject);")
29342941
return
29352942
}
29362943
if let name = returnType.referencedGenericName {
@@ -2997,14 +3004,6 @@ extension BridgeJSLink {
29973004
return try call(calleeExpr: calleeExpr)
29983005
}
29993006

3000-
/// Generates an async method call with resolve/reject closure refs.
3001-
func callAsyncMethod(name: String) {
3002-
let objectExpr = "\(JSGlueVariableScope.reservedSwift).memory.getObject(self)"
3003-
let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name)
3004-
let callExpr = "\(calleeExpr)(\(parameterForwardings.joined(separator: ", ")))"
3005-
body.write("\(callExpr).then(resolve, reject);")
3006-
}
3007-
30083007
func callStaticMethod(on objectExpr: String, name: String) throws {
30093008
let calleeExpr = Self.propertyAccessExpr(objectExpr: objectExpr, propertyName: name)
30103009
return try call(calleeExpr: calleeExpr)
@@ -4235,7 +4234,7 @@ extension BridgeJSLink {
42354234
method: ExportedFunction
42364235
) throws {
42374236
let thunkBuilder = ImportedThunkBuilder(
4238-
effects: Effects(isAsync: false, isThrows: false),
4237+
effects: method.effects,
42394238
returnType: method.returnType,
42404239
context: .exportSwift,
42414240
intrinsicRegistry: intrinsicRegistry

‎Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ public protocol BridgeSkeletonVisitor {
607607
accessLevel: BridgeJSAccessLevel
608608
)
609609
mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton)
610+
mutating func visitProtocolMethod(_ method: ExportedFunction, accessLevel: BridgeJSAccessLevel)
610611
}
611612

612613
public extension BridgeSkeletonVisitor {
@@ -616,6 +617,7 @@ public extension BridgeSkeletonVisitor {
616617
accessLevel: BridgeJSAccessLevel
617618
) {}
618619
mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) {}
620+
mutating func visitProtocolMethod(_ method: ExportedFunction, accessLevel: BridgeJSAccessLevel) {}
619621
}
620622

621623
public struct BridgeSkeletonWalker<Visitor: BridgeSkeletonVisitor> {
@@ -681,6 +683,7 @@ public struct BridgeSkeletonWalker<Visitor: BridgeSkeletonVisitor> {
681683
}
682684
for proto in skeleton.protocols {
683685
for method in proto.methods {
686+
visitor.visitProtocolMethod(method, accessLevel: currentAccessLevel)
684687
walk(method)
685688
}
686689
for property in proto.properties {
@@ -1870,6 +1873,12 @@ public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor {
18701873
)
18711874
}
18721875

1876+
public mutating func visitProtocolMethod(_ method: ExportedFunction, accessLevel: BridgeJSAccessLevel) {
1877+
guard method.effects.isAsync else { return }
1878+
1879+
recordInjectedSignatures(forReturnType: method.returnType, accessLevel: accessLevel)
1880+
}
1881+
18731882
private mutating func recordInjectedSignatures(
18741883
forReturnType returnType: BridgeType,
18751884
accessLevel: BridgeJSAccessLevel

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ import JavaScriptKit
5959
func createEnum() -> ExampleEnum
6060
func handleResult(_ result: Result)
6161
func getResult() -> Result
62+
func validate(_ value: Int) throws(JSException) -> Int
63+
func loadLabel(_ key: String) async throws(JSException) -> String
64+
func reset() async throws(JSException)
65+
func fetchCount() async -> Int
6266
}
6367

6468
@JS class MyViewController {
@@ -95,6 +99,14 @@ import JavaScriptKit
9599
@JS func sendHelper(_ helper: Helper) {
96100
delegate.onHelperUpdated(helper)
97101
}
102+
103+
@JS func validateValue(_ value: Int) throws(JSException) -> Int {
104+
return try delegate.validate(value)
105+
}
106+
107+
@JS func loadLabel(_ key: String) async throws(JSException) -> String {
108+
return try await delegate.loadLabel(key)
109+
}
98110
}
99111

100112
// Protocol array support

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Protocol.json‎

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,62 @@
228228
"returnType" : {
229229
"void" : {
230230

231+
}
232+
}
233+
},
234+
{
235+
"abiName" : "bjs_MyViewController_validateValue",
236+
"effects" : {
237+
"isAsync" : false,
238+
"isStatic" : false,
239+
"isThrows" : true
240+
},
241+
"name" : "validateValue",
242+
"parameters" : [
243+
{
244+
"label" : "_",
245+
"name" : "value",
246+
"type" : {
247+
"integer" : {
248+
"_0" : {
249+
"isSigned" : true,
250+
"width" : "word"
251+
}
252+
}
253+
}
254+
}
255+
],
256+
"returnType" : {
257+
"integer" : {
258+
"_0" : {
259+
"isSigned" : true,
260+
"width" : "word"
261+
}
262+
}
263+
}
264+
},
265+
{
266+
"abiName" : "bjs_MyViewController_loadLabel",
267+
"effects" : {
268+
"isAsync" : true,
269+
"isStatic" : false,
270+
"isThrows" : true
271+
},
272+
"name" : "loadLabel",
273+
"parameters" : [
274+
{
275+
"label" : "_",
276+
"name" : "key",
277+
"type" : {
278+
"string" : {
279+
280+
}
281+
}
282+
}
283+
],
284+
"returnType" : {
285+
"string" : {
286+
231287
}
232288
}
233289
}
@@ -831,6 +887,99 @@
831887
"_0" : "Result"
832888
}
833889
}
890+
},
891+
{
892+
"abiName" : "bjs_MyViewControllerDelegate_validate",
893+
"effects" : {
894+
"isAsync" : false,
895+
"isStatic" : false,
896+
"isThrows" : true
897+
},
898+
"name" : "validate",
899+
"parameters" : [
900+
{
901+
"label" : "_",
902+
"name" : "value",
903+
"type" : {
904+
"integer" : {
905+
"_0" : {
906+
"isSigned" : true,
907+
"width" : "word"
908+
}
909+
}
910+
}
911+
}
912+
],
913+
"returnType" : {
914+
"integer" : {
915+
"_0" : {
916+
"isSigned" : true,
917+
"width" : "word"
918+
}
919+
}
920+
}
921+
},
922+
{
923+
"abiName" : "bjs_MyViewControllerDelegate_loadLabel",
924+
"effects" : {
925+
"isAsync" : true,
926+
"isStatic" : false,
927+
"isThrows" : true
928+
},
929+
"name" : "loadLabel",
930+
"parameters" : [
931+
{
932+
"label" : "_",
933+
"name" : "key",
934+
"type" : {
935+
"string" : {
936+
937+
}
938+
}
939+
}
940+
],
941+
"returnType" : {
942+
"string" : {
943+
944+
}
945+
}
946+
},
947+
{
948+
"abiName" : "bjs_MyViewControllerDelegate_reset",
949+
"effects" : {
950+
"isAsync" : true,
951+
"isStatic" : false,
952+
"isThrows" : true
953+
},
954+
"name" : "reset",
955+
"parameters" : [
956+
957+
],
958+
"returnType" : {
959+
"void" : {
960+
961+
}
962+
}
963+
},
964+
{
965+
"abiName" : "bjs_MyViewControllerDelegate_fetchCount",
966+
"effects" : {
967+
"isAsync" : true,
968+
"isStatic" : false,
969+
"isThrows" : false
970+
},
971+
"name" : "fetchCount",
972+
"parameters" : [
973+
974+
],
975+
"returnType" : {
976+
"integer" : {
977+
"_0" : {
978+
"isSigned" : true,
979+
"width" : "word"
980+
}
981+
}
982+
}
834983
}
835984
],
836985
"name" : "MyViewControllerDelegate",

0 commit comments

Comments
 (0)