@@ -155,14 +155,37 @@ class ExportSwift {
155
155
abiName = " bjs_ \( className) _ \( name) "
156
156
}
157
157
158
+ guard let effects = collectEffects ( signature: node. signature) else {
159
+ return nil
160
+ }
161
+
158
162
return ExportedFunction (
159
163
name: name,
160
164
abiName: abiName,
161
165
parameters: parameters,
162
- returnType: returnType
166
+ returnType: returnType,
167
+ effects: effects
163
168
)
164
169
}
165
170
171
+ private func collectEffects( signature: FunctionSignatureSyntax ) -> Effects ? {
172
+ let isAsync = signature. effectSpecifiers? . asyncSpecifier != nil
173
+ var isThrows = false
174
+ if let throwsClause: ThrowsClauseSyntax = signature. effectSpecifiers? . throwsClause {
175
+ // Limit the thrown type to JSException for now
176
+ guard let thrownType = throwsClause. type else {
177
+ diagnose ( node: throwsClause, message: " Thrown type is not specified, only JSException is supported for now " )
178
+ return nil
179
+ }
180
+ guard thrownType. trimmedDescription == " JSException " else {
181
+ diagnose ( node: throwsClause, message: " Only JSException is supported for thrown type, got \( thrownType. trimmedDescription) " )
182
+ return nil
183
+ }
184
+ isThrows = true
185
+ }
186
+ return Effects ( isAsync: isAsync, isThrows: isThrows)
187
+ }
188
+
166
189
override func visit( _ node: InitializerDeclSyntax ) -> SyntaxVisitorContinueKind {
167
190
guard node. attributes. hasJSAttribute ( ) else { return . skipChildren }
168
191
guard case . classBody( let name) = state else {
@@ -180,9 +203,14 @@ class ExportSwift {
180
203
parameters. append ( Parameter ( label: label, name: name, type: type) )
181
204
}
182
205
206
+ guard let effects = collectEffects ( signature: node. signature) else {
207
+ return . skipChildren
208
+ }
209
+
183
210
let constructor = ExportedConstructor (
184
211
abiName: " bjs_ \( name) _init " ,
185
- parameters: parameters
212
+ parameters: parameters,
213
+ effects: effects
186
214
)
187
215
exportedClasses [ name] ? . constructor = constructor
188
216
return . skipChildren
@@ -245,6 +273,8 @@ class ExportSwift {
245
273
246
274
@_extern(wasm, module: " bjs " , name: " swift_js_retain " )
247
275
private func _swift_js_retain(_ ptr: Int32) -> Int32
276
+ @_extern(wasm, module: " bjs " , name: " swift_js_throw " )
277
+ private func _swift_js_throw(_ id: Int32)
248
278
"""
249
279
250
280
func renderSwiftGlue( ) -> String ? {
@@ -268,6 +298,11 @@ class ExportSwift {
268
298
var abiParameterForwardings : [ LabeledExprSyntax ] = [ ]
269
299
var abiParameterSignatures : [ ( name: String , type: WasmCoreType ) ] = [ ]
270
300
var abiReturnType : WasmCoreType ?
301
+ let effects : Effects
302
+
303
+ init ( effects: Effects ) {
304
+ self . effects = effects
305
+ }
271
306
272
307
func liftParameter( param: Parameter ) {
273
308
switch param. type {
@@ -350,35 +385,36 @@ class ExportSwift {
350
385
}
351
386
}
352
387
353
- func call( name: String , returnType: BridgeType ) {
388
+ private func renderCallStatement( callee: ExprSyntax , returnType: BridgeType ) -> StmtSyntax {
389
+ var callExpr : ExprSyntax = " \( raw: callee) ( \( raw: abiParameterForwardings. map { $0. description } . joined ( separator: " , " ) ) ) "
390
+ if effects. isAsync {
391
+ callExpr = ExprSyntax ( AwaitExprSyntax ( awaitKeyword: . keyword( . await ) , expression: callExpr) )
392
+ }
393
+ if effects. isThrows {
394
+ callExpr = ExprSyntax ( TryExprSyntax (
395
+ tryKeyword: . keyword( . try ) . with ( \. trailingTrivia, . space) ,
396
+ expression: callExpr
397
+ ) )
398
+ }
354
399
let retMutability = returnType == . string ? " var " : " let "
355
- let callExpr : ExprSyntax =
356
- " \( raw: name) ( \( raw: abiParameterForwardings. map { $0. description } . joined ( separator: " , " ) ) ) "
357
400
if returnType == . void {
358
- body . append ( " \( raw: callExpr) " )
401
+ return StmtSyntax ( " \( raw: callExpr) " )
359
402
} else {
360
- body. append (
361
- """
362
- \( raw: retMutability) ret = \( raw: callExpr)
363
- """
364
- )
403
+ return StmtSyntax ( " \( raw: retMutability) ret = \( raw: callExpr) " )
365
404
}
366
405
}
367
406
407
+ func call( name: String , returnType: BridgeType ) {
408
+ let stmt = renderCallStatement ( callee: " \( raw: name) " , returnType: returnType)
409
+ body. append ( CodeBlockItemSyntax ( item: . stmt( stmt) ) )
410
+ }
411
+
368
412
func callMethod( klassName: String , methodName: String , returnType: BridgeType ) {
369
413
let _selfParam = self . abiParameterForwardings. removeFirst ( )
370
- let retMutability = returnType == . string ? " var " : " let "
371
- let callExpr : ExprSyntax =
372
- " \( raw: _selfParam) . \( raw: methodName) ( \( raw: abiParameterForwardings. map { $0. description } . joined ( separator: " , " ) ) ) "
373
- if returnType == . void {
374
- body. append ( " \( raw: callExpr) " )
375
- } else {
376
- body. append (
377
- """
378
- \( raw: retMutability) ret = \( raw: callExpr)
379
- """
380
- )
381
- }
414
+ let stmt = renderCallStatement (
415
+ callee: " \( raw: _selfParam) . \( raw: methodName) " , returnType: returnType
416
+ )
417
+ body. append ( CodeBlockItemSyntax ( item: . stmt( stmt) ) )
382
418
}
383
419
384
420
func lowerReturnValue( returnType: BridgeType ) {
@@ -440,19 +476,54 @@ class ExportSwift {
440
476
}
441
477
442
478
func render( abiName: String ) -> DeclSyntax {
479
+ let body : CodeBlockItemListSyntax
480
+ if effects. isThrows {
481
+ body = """
482
+ do {
483
+ \( CodeBlockItemListSyntax ( self . body) )
484
+ } catch let error {
485
+ if let error = error.thrownValue.object {
486
+ withExtendedLifetime(error) {
487
+ _swift_js_throw(Int32(bitPattern: $0.id))
488
+ }
489
+ } else {
490
+ let jsError = JSError(message: String(describing: error))
491
+ withExtendedLifetime(jsError.jsObject) {
492
+ _swift_js_throw(Int32(bitPattern: $0.id))
493
+ }
494
+ }
495
+ \( raw: returnPlaceholderStmt ( ) )
496
+ }
497
+ """
498
+ } else {
499
+ body = CodeBlockItemListSyntax ( self . body)
500
+ }
443
501
return """
444
502
@_expose(wasm, " \( raw: abiName) " )
445
503
@_cdecl( " \( raw: abiName) " )
446
504
public func _ \( raw: abiName) ( \( raw: parameterSignature ( ) ) ) -> \( raw: returnSignature ( ) ) {
447
- \( CodeBlockItemListSyntax ( body) )
505
+ \( body)
448
506
}
449
507
"""
450
508
}
451
509
510
+ private func returnPlaceholderStmt( ) -> String {
511
+ switch abiReturnType {
512
+ case . i32: return " return 0 "
513
+ case . i64: return " return 0 "
514
+ case . f32: return " return 0.0 "
515
+ case . f64: return " return 0.0 "
516
+ case . pointer: return " return UnsafeMutableRawPointer(bitPattern: -1) "
517
+ case . none: return " return "
518
+ }
519
+ }
520
+
452
521
func parameterSignature( ) -> String {
453
- abiParameterSignatures. map { " \( $0. name) : \( $0. type. swiftType) " } . joined (
454
- separator: " , "
455
- )
522
+ var nameAndType : [ ( name: String , abiType: String ) ] = [ ]
523
+ for (name, type) in abiParameterSignatures {
524
+ nameAndType. append ( ( name, type. swiftType) )
525
+ }
526
+ return nameAndType. map { " \( $0. name) : \( $0. abiType) " } . joined ( separator: " , " )
456
527
}
457
528
458
529
func returnSignature( ) -> String {
@@ -461,7 +532,7 @@ class ExportSwift {
461
532
}
462
533
463
534
func renderSingleExportedFunction( function: ExportedFunction ) -> DeclSyntax {
464
- let builder = ExportedThunkBuilder ( )
535
+ let builder = ExportedThunkBuilder ( effects : function . effects )
465
536
for param in function. parameters {
466
537
builder. liftParameter ( param: param)
467
538
}
@@ -520,7 +591,7 @@ class ExportSwift {
520
591
func renderSingleExportedClass( klass: ExportedClass ) -> [ DeclSyntax ] {
521
592
var decls : [ DeclSyntax ] = [ ]
522
593
if let constructor = klass. constructor {
523
- let builder = ExportedThunkBuilder ( )
594
+ let builder = ExportedThunkBuilder ( effects : constructor . effects )
524
595
for param in constructor. parameters {
525
596
builder. liftParameter ( param: param)
526
597
}
@@ -529,7 +600,7 @@ class ExportSwift {
529
600
decls. append ( builder. render ( abiName: constructor. abiName) )
530
601
}
531
602
for method in klass. methods {
532
- let builder = ExportedThunkBuilder ( )
603
+ let builder = ExportedThunkBuilder ( effects : method . effects )
533
604
builder. liftParameter (
534
605
param: Parameter ( label: nil , name: " _self " , type: . swiftHeapObject( klass. name) )
535
606
)
0 commit comments