-
Notifications
You must be signed in to change notification settings - Fork 3
Support try-catch-finally #32
Description
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)))))
)