Skip to content
This repository was archived by the owner on Jul 12, 2024. It is now read-only.
This repository was archived by the owner on Jul 12, 2024. It is now read-only.

Support try-catch-finally #32

@tanishiking

Description

@tanishiking

Now, we should be able to support try-catch-finally using Wasm EH and availability of java.lang.Throwable

We should be able to catch the JavaScript exception in future(?), but the initial focus should be on catching the exceptions thrown in Wasm.

We'll include only one exception tag within the module, whose type is java.lang.Throwable. Whenever an exception is thrown, it will always compile to throw 0 with an operand of type (or subtype of) java.lang.Throwable.

(tag $tag (param (ref null $java.lang.Throwable)))) ;; whose tag idx is 0

and the catch clause in Wasm always catch the java.lang.Throwable with catch 0, and then validate the exception's type. If none of catch clauses (in Scala) caught an exception, rethrow the exception.

For example,

try-catch

// ExceptionA extends Exception
// ExceptionB extends Exception
try {
  throw Exception()
} catch (e: ExceptionA) {
} catch (e: ExceptionB) {
}

should be compiled to

(local $ex (ref null $java.lang.Throwable)
try  
    ;; construct the Exception and push to the stack
    throw 0
catch 0
    local.tee $ex ;; thrown exception
    ref.test $ExceptionA ;; test if the thrown exception is a subtype of ExceptionA
    if ;; catch(e: ExceptionA) { ... }
        ;; ...
    else
        local.get $ex
        ref.test $ExceptionB
        if ;; catch (e: ExceptionB) { ... }
            ;; ...
            else ;; if none of catch clauses catch the exception
                local.get $ex
                throw 0 ;; rethrow it
            end
        end
    end)

try-catch-finally

try {
    throw Exception()
} catch (e: Exception) {
} finally { println("hello") }
block $finally
    try
        try
            ;; construct exception
            throw 0
        catch 0
             local.tee $ex
             ref.test $Exception
             if
                 ;; catch(e: Exception) { ... }
             else ;; catch clauses couldn't capture the exeption rethrow
                 local.get $ex
                 throw 0
              end
              ;; reach here if one of the catch clause captured the exception
              br $finally ;; jump to outside of the block and run finally block
         end
         unreachable
    catch 0 ;; if the exception isn't caught by any catch clauses, do something in finally clause and rethrow the exception.
          ;; println("hello") (finally clause)
          ;; push the caught exception to stack
          throw 0
     end
     unreachable
end ;; of block
;; println("hello") finally clause

note: it seems try-catch-finally is compiled to something above in IR ❤️

    val x = try {
        val x = 1
    } catch {
        case e: ExceptionA => js.Dynamic.global.console.log("finally")
        case e: ExceptionB => js.Dynamic.global.console.log("finally")
    } finally {
        js.Dynamic.global.console.log("finally")
    }

TryFinally(
  TryCatch(
    Block(
      VarDef(LocalIdent(LocalName<x>),NoOriginalName,IntType,false,IntLiteral(1)),
      Undefined()
    ),
    LocalIdent(LocalName<e>),
    NoOriginalName,
    If(
      IsInstanceOf(
        VarRef(LocalIdent(LocalName<e>)),
        ClassType(ClassName<sample.ExceptionA>)
      ),
      Block(
        VarDef(LocalIdent(LocalName<e$3>),OriginalName(e),ClassType(ClassName<sample.ExceptionA>),false,AsInstanceOf(VarRef(LocalIdent(LocalName<e>)),ClassType(ClassName<sample.ExceptionA>))),
        JSMethodApply(JSGlobalRef(console),StringLiteral(log),List(Apply(org.scalajs.ir.Trees$ApplyFlags@0,LoadModule(ClassName<scala.scalajs.js.Any$>),MethodIdent(MethodName<fromString;Ljava.lang.String;Lscala.scalajs.js.Any>),List(StringLiteral(finally)))))
      ),
      If(
        IsInstanceOf(VarRef(LocalIdent(LocalName<e>)),ClassType(ClassName<sample.ExceptionB>)),
        Block(
          VarDef(LocalIdent(LocalName<e$2>),OriginalName(e),ClassType(ClassName<sample.ExceptionB>),false,AsInstanceOf(VarRef(LocalIdent(LocalName<e>)),ClassType(ClassName<sample.ExceptionB>))),
          JSMethodApply(JSGlobalRef(console),StringLiteral(log),List(Apply(org.scalajs.ir.Trees$ApplyFlags@0,LoadModule(ClassName<scala.scalajs.js.Any$>),MethodIdent(MethodName<fromString;Ljava.lang.String;Lscala.scalajs.js.Any>),List(StringLiteral(finally)))))),
        Throw(VarRef(LocalIdent(LocalName<e>)))
      )
    )
  ),
  JSMethodApply(JSGlobalRef(console),StringLiteral(log),List(Apply(org.scalajs.ir.Trees$ApplyFlags@0,LoadModule(ClassName<scala.scalajs.js.Any$>),MethodIdent(MethodName<fromString;Ljava.lang.String;Lscala.scalajs.js.Any>),List(StringLiteral(finally)))))
)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions