Skip to content

Commit ea82047

Browse files
committed
BridgeJS: Support throwing any error conforming to ConvertibleToJSException
1 parent ba66ff1 commit ea82047

File tree

21 files changed

+491
-176
lines changed

21 files changed

+491
-176
lines changed

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,16 +182,7 @@ public func _bjs_PlayBridgeJS_updateDetailed(_ _self: UnsafeMutableRawPointer, _
182182
let ret = try PlayBridgeJS.bridgeJSLiftParameter(_self).updateDetailed(swiftSource: String.bridgeJSLiftParameter(swiftSourceBytes, swiftSourceLength), dtsSource: String.bridgeJSLiftParameter(dtsSourceBytes, dtsSourceLength))
183183
return ret.bridgeJSLowerReturn()
184184
} catch let error {
185-
if let error = error.thrownValue.object {
186-
withExtendedLifetime(error) {
187-
_swift_js_throw(Int32(bitPattern: $0.id))
188-
}
189-
} else {
190-
let jsError = JSError(message: String(describing: error))
191-
withExtendedLifetime(jsError.jsObject) {
192-
_swift_js_throw(Int32(bitPattern: $0.id))
193-
}
194-
}
185+
error.bridgeJSLowerThrow()
195186
return
196187
}
197188
#else

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -318,20 +318,25 @@ public class ExportSwift {
318318

319319
func render(abiName: String) -> DeclSyntax {
320320
let body: CodeBlockItemListSyntax
321-
if effects.isAsync {
321+
if effects.isAsync, effects.isThrows {
322322
// Explicit closure type annotation needed when throws is present
323323
// so Swift infers throws(JSException) instead of throws(any Error)
324324
// See: https://github.com/swiftlang/swift/issues/76165
325-
let closureHead: String
326-
if effects.isThrows {
327-
let hasReturn = self.body.contains { $0.description.contains("return ") }
328-
let ret = hasReturn ? " -> JSValue" : ""
329-
closureHead = " () async throws(JSException)\(ret) in"
330-
} else {
331-
closureHead = ""
332-
}
325+
let hasReturn = self.body.contains { $0.description.contains("return ") }
326+
let ret = hasReturn ? " -> JSValue" : ""
333327
body = """
334-
let ret = JSPromise.async {\(raw: closureHead)
328+
let ret = JSPromise.async { () async throws(JSException)\(raw: ret) in
329+
do {
330+
\(CodeBlockItemListSyntax(self.body))
331+
} catch let error {
332+
throw error.bridgeJSLowerThrowAsync()
333+
}
334+
}.jsObject
335+
return ret.bridgeJSLowerReturn()
336+
"""
337+
} else if effects.isAsync {
338+
body = """
339+
let ret = JSPromise.async {
335340
\(CodeBlockItemListSyntax(self.body))
336341
}.jsObject
337342
return ret.bridgeJSLowerReturn()
@@ -341,16 +346,7 @@ public class ExportSwift {
341346
do {
342347
\(CodeBlockItemListSyntax(self.body))
343348
} catch let error {
344-
if let error = error.thrownValue.object {
345-
withExtendedLifetime(error) {
346-
_swift_js_throw(Int32(bitPattern: $0.id))
347-
}
348-
} else {
349-
let jsError = JSError(message: String(describing: error))
350-
withExtendedLifetime(jsError.jsObject) {
351-
_swift_js_throw(Int32(bitPattern: $0.id))
352-
}
353-
}
349+
error.bridgeJSLowerThrow()
354350
\(raw: returnPlaceholderStmt())
355351
}
356352
"""

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,18 +1122,11 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
11221122
let isAsync = signature.effectSpecifiers?.asyncSpecifier != nil
11231123
var isThrows = false
11241124
if let throwsClause: ThrowsClauseSyntax = signature.effectSpecifiers?.throwsClause {
1125-
// Limit the thrown type to JSException for now
1126-
guard let thrownType = throwsClause.type else {
1125+
// Require typed throws for now
1126+
guard throwsClause.type != nil else {
11271127
diagnose(
11281128
node: throwsClause,
1129-
message: "Thrown type is not specified, only JSException is supported for now"
1130-
)
1131-
return nil
1132-
}
1133-
guard thrownType.trimmedDescription == "JSException" else {
1134-
diagnose(
1135-
node: throwsClause,
1136-
message: "Only JSException is supported for thrown type, got \(thrownType.trimmedDescription)"
1129+
message: "Thrown type must be specified. Only JSException or any error conforming to ConvertibleToJSException is supported"
11371130
)
11381131
return nil
11391132
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@JS func asyncThrowsVoid() async throws(JSException) {
2+
throw JSException(message: "TestError")
3+
}
4+
5+
@JS func asyncThrowsWithResult() async throws(JSException) -> Int {
6+
return 1
7+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"exported" : {
3+
"classes" : [
4+
5+
],
6+
"enums" : [
7+
8+
],
9+
"exposeToGlobal" : false,
10+
"functions" : [
11+
{
12+
"abiName" : "bjs_asyncThrowsVoid",
13+
"effects" : {
14+
"isAsync" : true,
15+
"isStatic" : false,
16+
"isThrows" : true
17+
},
18+
"name" : "asyncThrowsVoid",
19+
"parameters" : [
20+
21+
],
22+
"returnType" : {
23+
"void" : {
24+
25+
}
26+
}
27+
},
28+
{
29+
"abiName" : "bjs_asyncThrowsWithResult",
30+
"effects" : {
31+
"isAsync" : true,
32+
"isStatic" : false,
33+
"isThrows" : true
34+
},
35+
"name" : "asyncThrowsWithResult",
36+
"parameters" : [
37+
38+
],
39+
"returnType" : {
40+
"int" : {
41+
42+
}
43+
}
44+
}
45+
],
46+
"protocols" : [
47+
48+
],
49+
"structs" : [
50+
51+
]
52+
},
53+
"moduleName" : "TestModule"
54+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@_expose(wasm, "bjs_asyncThrowsVoid")
2+
@_cdecl("bjs_asyncThrowsVoid")
3+
public func _bjs_asyncThrowsVoid() -> Int32 {
4+
#if arch(wasm32)
5+
let ret = JSPromise.async { () async throws(JSException) in
6+
do {
7+
try await asyncThrowsVoid()
8+
} catch let error {
9+
throw error.bridgeJSLowerThrowAsync()
10+
}
11+
}.jsObject
12+
return ret.bridgeJSLowerReturn()
13+
#else
14+
fatalError("Only available on WebAssembly")
15+
#endif
16+
}
17+
18+
@_expose(wasm, "bjs_asyncThrowsWithResult")
19+
@_cdecl("bjs_asyncThrowsWithResult")
20+
public func _bjs_asyncThrowsWithResult() -> Int32 {
21+
#if arch(wasm32)
22+
let ret = JSPromise.async { () async throws(JSException) -> JSValue in
23+
do {
24+
return try await asyncThrowsWithResult().jsValue
25+
} catch let error {
26+
throw error.bridgeJSLowerThrowAsync()
27+
}
28+
}.jsObject
29+
return ret.bridgeJSLowerReturn()
30+
#else
31+
fatalError("Only available on WebAssembly")
32+
#endif
33+
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumNamespace.Global.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,7 @@ public func _bjs_Services_Graph_GraphOperations_static_validate(_ graphId: Int32
112112
let ret = try GraphOperations.validate(graphId: Int.bridgeJSLiftParameter(graphId))
113113
return ret.bridgeJSLowerReturn()
114114
} catch let error {
115-
if let error = error.thrownValue.object {
116-
withExtendedLifetime(error) {
117-
_swift_js_throw(Int32(bitPattern: $0.id))
118-
}
119-
} else {
120-
let jsError = JSError(message: String(describing: error))
121-
withExtendedLifetime(jsError.jsObject) {
122-
_swift_js_throw(Int32(bitPattern: $0.id))
123-
}
124-
}
115+
error.bridgeJSLowerThrow()
125116
return 0
126117
}
127118
#else

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,7 @@ public func _bjs_Services_Graph_GraphOperations_static_validate(_ graphId: Int32
112112
let ret = try GraphOperations.validate(graphId: Int.bridgeJSLiftParameter(graphId))
113113
return ret.bridgeJSLowerReturn()
114114
} catch let error {
115-
if let error = error.thrownValue.object {
116-
withExtendedLifetime(error) {
117-
_swift_js_throw(Int32(bitPattern: $0.id))
118-
}
119-
} else {
120-
let jsError = JSError(message: String(describing: error))
121-
withExtendedLifetime(jsError.jsObject) {
122-
_swift_js_throw(Int32(bitPattern: $0.id))
123-
}
124-
}
115+
error.bridgeJSLowerThrow()
125116
return 0
126117
}
127118
#else

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,7 @@ public func _bjs_makeFoo() -> Int32 {
5454
let ret = try makeFoo()
5555
return ret.bridgeJSLowerReturn()
5656
} catch let error {
57-
if let error = error.thrownValue.object {
58-
withExtendedLifetime(error) {
59-
_swift_js_throw(Int32(bitPattern: $0.id))
60-
}
61-
} else {
62-
let jsError = JSError(message: String(describing: error))
63-
withExtendedLifetime(jsError.jsObject) {
64-
_swift_js_throw(Int32(bitPattern: $0.id))
65-
}
66-
}
57+
error.bridgeJSLowerThrow()
6758
return 0
6859
}
6960
#else

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,7 @@ public func _bjs_throwsSomething() -> Void {
55
do {
66
try throwsSomething()
77
} catch let error {
8-
if let error = error.thrownValue.object {
9-
withExtendedLifetime(error) {
10-
_swift_js_throw(Int32(bitPattern: $0.id))
11-
}
12-
} else {
13-
let jsError = JSError(message: String(describing: error))
14-
withExtendedLifetime(jsError.jsObject) {
15-
_swift_js_throw(Int32(bitPattern: $0.id))
16-
}
17-
}
8+
error.bridgeJSLowerThrow()
189
return
1910
}
2011
#else

0 commit comments

Comments
 (0)