@@ -155,14 +155,37 @@ class ExportSwift {
155155 abiName = " bjs_ \( className) _ \( name) "
156156 }
157157
158+ guard let effects = collectEffects ( signature: node. signature) else {
159+ return nil
160+ }
161+
158162 return ExportedFunction (
159163 name: name,
160164 abiName: abiName,
161165 parameters: parameters,
162- returnType: returnType
166+ returnType: returnType,
167+ effects: effects
163168 )
164169 }
165170
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+
166189 override func visit( _ node: InitializerDeclSyntax ) -> SyntaxVisitorContinueKind {
167190 guard node. attributes. hasJSAttribute ( ) else { return . skipChildren }
168191 guard case . classBody( let name) = state else {
@@ -180,9 +203,14 @@ class ExportSwift {
180203 parameters. append ( Parameter ( label: label, name: name, type: type) )
181204 }
182205
206+ guard let effects = collectEffects ( signature: node. signature) else {
207+ return . skipChildren
208+ }
209+
183210 let constructor = ExportedConstructor (
184211 abiName: " bjs_ \( name) _init " ,
185- parameters: parameters
212+ parameters: parameters,
213+ effects: effects
186214 )
187215 exportedClasses [ name] ? . constructor = constructor
188216 return . skipChildren
@@ -245,6 +273,8 @@ class ExportSwift {
245273
246274 @_extern(wasm, module: " bjs " , name: " swift_js_retain " )
247275 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)
248278 """
249279
250280 func renderSwiftGlue( ) -> String ? {
@@ -268,6 +298,11 @@ class ExportSwift {
268298 var abiParameterForwardings : [ LabeledExprSyntax ] = [ ]
269299 var abiParameterSignatures : [ ( name: String , type: WasmCoreType ) ] = [ ]
270300 var abiReturnType : WasmCoreType ?
301+ let effects : Effects
302+
303+ init ( effects: Effects ) {
304+ self . effects = effects
305+ }
271306
272307 func liftParameter( param: Parameter ) {
273308 switch param. type {
@@ -350,35 +385,36 @@ class ExportSwift {
350385 }
351386 }
352387
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+ }
354399 let retMutability = returnType == . string ? " var " : " let "
355- let callExpr : ExprSyntax =
356- " \( raw: name) ( \( raw: abiParameterForwardings. map { $0. description } . joined ( separator: " , " ) ) ) "
357400 if returnType == . void {
358- body . append ( " \( raw: callExpr) " )
401+ return StmtSyntax ( " \( raw: callExpr) " )
359402 } else {
360- body. append (
361- """
362- \( raw: retMutability) ret = \( raw: callExpr)
363- """
364- )
403+ return StmtSyntax ( " \( raw: retMutability) ret = \( raw: callExpr) " )
365404 }
366405 }
367406
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+
368412 func callMethod( klassName: String , methodName: String , returnType: BridgeType ) {
369413 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) ) )
382418 }
383419
384420 func lowerReturnValue( returnType: BridgeType ) {
@@ -440,19 +476,54 @@ class ExportSwift {
440476 }
441477
442478 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+ }
443501 return """
444502 @_expose(wasm, " \( raw: abiName) " )
445503 @_cdecl( " \( raw: abiName) " )
446504 public func _ \( raw: abiName) ( \( raw: parameterSignature ( ) ) ) -> \( raw: returnSignature ( ) ) {
447- \( CodeBlockItemListSyntax ( body) )
505+ \( body)
448506 }
449507 """
450508 }
451509
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+
452521 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: " , " )
456527 }
457528
458529 func returnSignature( ) -> String {
@@ -461,7 +532,7 @@ class ExportSwift {
461532 }
462533
463534 func renderSingleExportedFunction( function: ExportedFunction ) -> DeclSyntax {
464- let builder = ExportedThunkBuilder ( )
535+ let builder = ExportedThunkBuilder ( effects : function . effects )
465536 for param in function. parameters {
466537 builder. liftParameter ( param: param)
467538 }
@@ -520,7 +591,7 @@ class ExportSwift {
520591 func renderSingleExportedClass( klass: ExportedClass ) -> [ DeclSyntax ] {
521592 var decls : [ DeclSyntax ] = [ ]
522593 if let constructor = klass. constructor {
523- let builder = ExportedThunkBuilder ( )
594+ let builder = ExportedThunkBuilder ( effects : constructor . effects )
524595 for param in constructor. parameters {
525596 builder. liftParameter ( param: param)
526597 }
@@ -529,7 +600,7 @@ class ExportSwift {
529600 decls. append ( builder. render ( abiName: constructor. abiName) )
530601 }
531602 for method in klass. methods {
532- let builder = ExportedThunkBuilder ( )
603+ let builder = ExportedThunkBuilder ( effects : method . effects )
533604 builder. liftParameter (
534605 param: Parameter ( label: nil , name: " _self " , type: . swiftHeapObject( klass. name) )
535606 )
0 commit comments