Skip to content

Commit 5b56aa4

Browse files
Merge pull request #390 from swiftwasm/yt/fix-bridgejs-test-xcode
BridgeJS: Add `throws(JSException)` to imported methods
2 parents 70b66c2 + 818fa91 commit 5b56aa4

File tree

52 files changed

+814
-199
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+814
-199
lines changed

Benchmarks/Sources/Generated/BridgeJS.ImportTS.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
@_spi(BridgeJS) import JavaScriptKit
88

9-
func benchmarkHelperNoop() -> Void {
9+
func benchmarkHelperNoop() throws(JSException) -> Void {
1010
#if arch(wasm32)
1111
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoop")
1212
func bjs_benchmarkHelperNoop() -> Void
@@ -16,9 +16,12 @@ func benchmarkHelperNoop() -> Void {
1616
}
1717
#endif
1818
bjs_benchmarkHelperNoop()
19+
if let error = _swift_js_take_exception() {
20+
throw error
21+
}
1922
}
2023

21-
func benchmarkHelperNoopWithNumber(_ n: Double) -> Void {
24+
func benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void {
2225
#if arch(wasm32)
2326
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoopWithNumber")
2427
func bjs_benchmarkHelperNoopWithNumber(_ n: Float64) -> Void
@@ -28,9 +31,12 @@ func benchmarkHelperNoopWithNumber(_ n: Double) -> Void {
2831
}
2932
#endif
3033
bjs_benchmarkHelperNoopWithNumber(n)
34+
if let error = _swift_js_take_exception() {
35+
throw error
36+
}
3137
}
3238

33-
func benchmarkRunner(_ name: String, _ body: JSObject) -> Void {
39+
func benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Void {
3440
#if arch(wasm32)
3541
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner")
3642
func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void
@@ -44,4 +50,7 @@ func benchmarkRunner(_ name: String, _ body: JSObject) -> Void {
4450
_swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
4551
}
4652
bjs_benchmarkRunner(nameId, Int32(bitPattern: body.id))
53+
if let error = _swift_js_take_exception() {
54+
throw error
55+
}
4756
}

Examples/PlayBridgeJS/Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ let package = Package(
2222
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
2323
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
2424
],
25+
exclude: [
26+
"bridge-js.d.ts",
27+
"bridge-js.config.json",
28+
"Generated/JavaScript",
29+
],
2530
swiftSettings: [
2631
.enableExperimentalFeature("Extern")
2732
],

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public func _bjs_PlayBridgeJS_update(_self: UnsafeMutableRawPointer, swiftSource
2626
_swift_js_init_memory(swiftSourceBytes, b.baseAddress.unsafelyUnwrapped)
2727
return Int(swiftSourceLen)
2828
}
29-
let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in
29+
let dtsSource = String(unsafeUninitializedCapacity: Int(dtsSourceLen)) { b in
3030
_swift_js_init_memory(dtsSourceBytes, b.baseAddress.unsafelyUnwrapped)
3131
return Int(dtsSourceLen)
3232
}
33-
let ret = try Unmanaged<PlayBridgeJS>.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource)
34-
return Unmanaged.passRetained(ret).toOpaque()
33+
let ret = try Unmanaged<PlayBridgeJS>.fromOpaque(_self).takeUnretainedValue().update(swiftSource: swiftSource, dtsSource: dtsSource)
34+
return Unmanaged.passRetained(ret).toOpaque()
3535
} catch let error {
3636
if let error = error.thrownValue.object {
3737
withExtendedLifetime(error) {

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
@_spi(BridgeJS) import JavaScriptKit
88

9-
func createTS2Skeleton() -> TS2Skeleton {
9+
func createTS2Skeleton() throws(JSException) -> TS2Skeleton {
1010
#if arch(wasm32)
1111
@_extern(wasm, module: "PlayBridgeJS", name: "bjs_createTS2Skeleton")
1212
func bjs_createTS2Skeleton() -> Int32
@@ -16,6 +16,9 @@ func createTS2Skeleton() -> TS2Skeleton {
1616
}
1717
#endif
1818
let ret = bjs_createTS2Skeleton()
19+
if let error = _swift_js_take_exception() {
20+
throw error
21+
}
1922
return TS2Skeleton(takingThis: ret)
2023
}
2124

@@ -30,7 +33,7 @@ struct TS2Skeleton {
3033
self.this = JSObject(id: UInt32(bitPattern: this))
3134
}
3235

33-
func convert(_ ts: String) -> String {
36+
func convert(_ ts: String) throws(JSException) -> String {
3437
#if arch(wasm32)
3538
@_extern(wasm, module: "PlayBridgeJS", name: "bjs_TS2Skeleton_convert")
3639
func bjs_TS2Skeleton_convert(_ self: Int32, _ ts: Int32) -> Int32
@@ -44,6 +47,9 @@ struct TS2Skeleton {
4447
_swift_js_make_js_string(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
4548
}
4649
let ret = bjs_TS2Skeleton_convert(Int32(bitPattern: self.this.id), tsId)
50+
if let error = _swift_js_take_exception() {
51+
throw error
52+
}
4753
return String(unsafeUninitializedCapacity: Int(ret)) { b in
4854
_swift_js_init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret))
4955
return Int(ret)

Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import class Foundation.JSONDecoder
99
@JS func update(swiftSource: String, dtsSource: String) throws(JSException) -> PlayBridgeJSOutput {
1010
do {
1111
return try _update(swiftSource: swiftSource, dtsSource: dtsSource)
12+
} catch let error as JSException {
13+
throw error
1214
} catch {
1315
throw JSException(message: String(describing: error))
1416
}
@@ -20,8 +22,8 @@ import class Foundation.JSONDecoder
2022
try exportSwift.addSourceFile(sourceFile, "Playground.swift")
2123
let exportResult = try exportSwift.finalize()
2224
var importTS = ImportTS(progress: .silent, moduleName: "Playground")
23-
let ts2skeleton = createTS2Skeleton()
24-
let skeletonJSONString = ts2skeleton.convert(dtsSource)
25+
let ts2skeleton = try createTS2Skeleton()
26+
let skeletonJSONString = try ts2skeleton.convert(dtsSource)
2527
let decoder = JSONDecoder()
2628
let importSkeleton = try decoder.decode(
2729
ImportedFileSkeleton.self,

Package.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,17 @@ let package = Package(
151151
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
152152
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
153153
],
154-
path: "Plugins/BridgeJS/Sources/BridgeJSTool"
154+
path: "Plugins/BridgeJS/Sources/BridgeJSTool",
155+
exclude: ["TS2Skeleton/JavaScript"]
155156
),
156157
.testTarget(
157158
name: "BridgeJSRuntimeTests",
158159
dependencies: ["JavaScriptKit"],
159-
exclude: ["Generated/JavaScript"],
160+
exclude: [
161+
"bridge-js.config.json",
162+
"bridge-js.d.ts",
163+
"Generated/JavaScript",
164+
],
160165
swiftSettings: [
161166
.enableExperimentalFeature("Extern")
162167
]

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class ExportSwift {
5656

5757
fileprivate final class APICollector: SyntaxAnyVisitor {
5858
var exportedFunctions: [ExportedFunction] = []
59-
var exportedClasses: [String: ExportedClass] = [:]
59+
/// The names of the exported classes, in the order they were written in the source file
60+
var exportedClassNames: [String] = []
61+
var exportedClassByName: [String: ExportedClass] = [:]
6062
var errors: [DiagnosticError] = []
6163

6264
enum State {
@@ -114,7 +116,7 @@ class ExportSwift {
114116
return .skipChildren
115117
case .classBody(let name):
116118
if let exportedFunction = visitFunction(node: node) {
117-
exportedClasses[name]?.methods.append(exportedFunction)
119+
exportedClassByName[name]?.methods.append(exportedFunction)
118120
}
119121
return .skipChildren
120122
}
@@ -217,7 +219,7 @@ class ExportSwift {
217219
parameters: parameters,
218220
effects: effects
219221
)
220-
exportedClasses[name]?.constructor = constructor
222+
exportedClassByName[name]?.constructor = constructor
221223
return .skipChildren
222224
}
223225

@@ -226,11 +228,12 @@ class ExportSwift {
226228
stateStack.push(state: .classBody(name: name))
227229

228230
guard node.attributes.hasJSAttribute() else { return .skipChildren }
229-
exportedClasses[name] = ExportedClass(
231+
exportedClassByName[name] = ExportedClass(
230232
name: name,
231233
constructor: nil,
232234
methods: []
233235
)
236+
exportedClassNames.append(name)
234237
return .visitChildren
235238
}
236239
override func visitPost(_ node: ClassDeclSyntax) {
@@ -242,7 +245,11 @@ class ExportSwift {
242245
let collector = APICollector(parent: self)
243246
collector.walk(sourceFile)
244247
exportedFunctions.append(contentsOf: collector.exportedFunctions)
245-
exportedClasses.append(contentsOf: collector.exportedClasses.values)
248+
exportedClasses.append(
249+
contentsOf: collector.exportedClassNames.map {
250+
collector.exportedClassByName[$0]!
251+
}
252+
)
246253
return collector.errors
247254
}
248255

@@ -426,25 +433,7 @@ class ExportSwift {
426433
}
427434

428435
func lowerReturnValue(returnType: BridgeType) {
429-
switch returnType {
430-
case .void:
431-
abiReturnType = nil
432-
case .bool:
433-
abiReturnType = .i32
434-
case .int:
435-
abiReturnType = .i32
436-
case .float:
437-
abiReturnType = .f32
438-
case .double:
439-
abiReturnType = .f64
440-
case .string:
441-
abiReturnType = nil
442-
case .jsObject:
443-
abiReturnType = .i32
444-
case .swiftHeapObject:
445-
// UnsafeMutableRawPointer is returned as an i32 pointer
446-
abiReturnType = .pointer
447-
}
436+
abiReturnType = returnType.abiReturnType
448437

449438
switch returnType {
450439
case .void: break

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ struct ImportTS {
151151
} else {
152152
body.append("let ret = \(raw: call)")
153153
}
154+
body.append("if let error = _swift_js_take_exception() { throw error }")
154155
}
155156

156157
func liftReturnValue(returnType: BridgeType) throws {
@@ -253,6 +254,7 @@ struct ImportTS {
253254
)
254255
}
255256
}),
257+
effectSpecifiers: ImportTS.buildFunctionEffect(throws: true, async: false),
256258
returnClause: ReturnClauseSyntax(
257259
arrow: .arrowToken(),
258260
type: IdentifierTypeSyntax(name: .identifier(returnType.swiftType))
@@ -280,7 +282,8 @@ struct ImportTS {
280282
)
281283
}
282284
}
283-
)
285+
),
286+
effectSpecifiers: ImportTS.buildFunctionEffect(throws: true, async: false)
284287
),
285288
bodyBuilder: {
286289
self.renderImportDecl()
@@ -364,38 +367,33 @@ struct ImportTS {
364367
try builder.liftReturnValue(returnType: property.type)
365368
return AccessorDeclSyntax(
366369
accessorSpecifier: .keyword(.get),
370+
effectSpecifiers: Self.buildAccessorEffect(throws: true, async: false),
367371
body: CodeBlockSyntax {
368372
builder.renderImportDecl()
369373
builder.body
370374
}
371375
)
372376
}
373377

374-
func renderSetterDecl(property: ImportedPropertySkeleton) throws -> AccessorDeclSyntax {
378+
func renderSetterDecl(property: ImportedPropertySkeleton) throws -> DeclSyntax {
375379
let builder = ImportedThunkBuilder(
376380
moduleName: moduleName,
377381
abiName: property.setterAbiName(context: type)
378382
)
383+
let newValue = Parameter(label: nil, name: "newValue", type: property.type)
379384
try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
380-
try builder.lowerParameter(param: Parameter(label: nil, name: "newValue", type: property.type))
385+
try builder.lowerParameter(param: newValue)
381386
builder.call(returnType: .void)
382-
return AccessorDeclSyntax(
383-
modifier: DeclModifierSyntax(name: .keyword(.nonmutating)),
384-
accessorSpecifier: .keyword(.set),
385-
body: CodeBlockSyntax {
386-
builder.renderImportDecl()
387-
builder.body
388-
}
387+
return builder.renderThunkDecl(
388+
name: "set\(property.name.capitalizedFirstLetter())",
389+
parameters: [newValue],
390+
returnType: .void
389391
)
390392
}
391393

392394
func renderPropertyDecl(property: ImportedPropertySkeleton) throws -> [DeclSyntax] {
393-
var accessorDecls: [AccessorDeclSyntax] = []
394-
accessorDecls.append(try renderGetterDecl(property: property))
395-
if !property.isReadonly {
396-
accessorDecls.append(try renderSetterDecl(property: property))
397-
}
398-
return [
395+
let accessorDecls: [AccessorDeclSyntax] = [try renderGetterDecl(property: property)]
396+
var decls: [DeclSyntax] = [
399397
DeclSyntax(
400398
VariableDeclSyntax(
401399
leadingTrivia: Self.renderDocumentation(documentation: property.documentation),
@@ -418,6 +416,10 @@ struct ImportTS {
418416
)
419417
)
420418
]
419+
if !property.isReadonly {
420+
decls.append(try renderSetterDecl(property: property))
421+
}
422+
return decls
421423
}
422424
let classDecl = try StructDeclSyntax(
423425
leadingTrivia: Self.renderDocumentation(documentation: type.documentation),
@@ -469,4 +471,36 @@ struct ImportTS {
469471
let lines = documentation.split { $0.isNewline }
470472
return Trivia(pieces: lines.flatMap { [TriviaPiece.docLineComment("/// \($0)"), .newlines(1)] })
471473
}
474+
475+
static func buildFunctionEffect(throws: Bool, async: Bool) -> FunctionEffectSpecifiersSyntax {
476+
return FunctionEffectSpecifiersSyntax(
477+
asyncSpecifier: `async` ? .keyword(.async) : nil,
478+
throwsClause: `throws`
479+
? ThrowsClauseSyntax(
480+
throwsSpecifier: .keyword(.throws),
481+
leftParen: .leftParenToken(),
482+
type: IdentifierTypeSyntax(name: .identifier("JSException")),
483+
rightParen: .rightParenToken()
484+
) : nil
485+
)
486+
}
487+
static func buildAccessorEffect(throws: Bool, async: Bool) -> AccessorEffectSpecifiersSyntax {
488+
return AccessorEffectSpecifiersSyntax(
489+
asyncSpecifier: `async` ? .keyword(.async) : nil,
490+
throwsClause: `throws`
491+
? ThrowsClauseSyntax(
492+
throwsSpecifier: .keyword(.throws),
493+
leftParen: .leftParenToken(),
494+
type: IdentifierTypeSyntax(name: .identifier("JSException")),
495+
rightParen: .rightParenToken()
496+
) : nil
497+
)
498+
}
499+
}
500+
501+
extension String {
502+
func capitalizedFirstLetter() -> String {
503+
guard !isEmpty else { return self }
504+
return prefix(1).uppercased() + dropFirst()
505+
}
472506
}

0 commit comments

Comments
 (0)