Skip to content

Commit 368ded1

Browse files
committed
Scala.js: Handle EmptyTree and Try in the back-end.
1 parent 304f0d8 commit 368ded1

File tree

1 file changed

+120
-9
lines changed

1 file changed

+120
-9
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

+120-9
Original file line numberDiff line numberDiff line change
@@ -860,8 +860,8 @@ class JSCodeGen()(implicit ctx: Context) {
860860
case WhileDo(cond, body) =>
861861
js.While(genExpr(cond), genStat(body))
862862

863-
/*case t: Try =>
864-
genTry(t, isStat)*/
863+
case t: Try =>
864+
genTry(t, isStat)
865865

866866
case app: Apply =>
867867
genApply(app, isStat)
@@ -1012,8 +1012,8 @@ class JSCodeGen()(implicit ctx: Context) {
10121012
case tree: Closure =>
10131013
genClosure(tree)
10141014

1015-
/*case EmptyTree =>
1016-
js.Skip()*/
1015+
case EmptyTree =>
1016+
js.Skip()
10171017

10181018
case _ =>
10191019
throw new FatalError("Unexpected tree in genExpr: " +
@@ -1051,6 +1051,114 @@ class JSCodeGen()(implicit ctx: Context) {
10511051
}
10521052
}
10531053

1054+
/** Gen IR code for a `try..catch` or `try..finally` block.
1055+
*
1056+
* `try..finally` blocks are compiled straightforwardly to `try..finally`
1057+
* blocks of the IR.
1058+
*
1059+
* `try..catch` blocks are a bit more subtle, as the IR does not have
1060+
* type-based selection of exceptions to catch. We thus encode explicitly
1061+
* the type tests, like in:
1062+
*
1063+
* ```
1064+
* try { ... }
1065+
* catch (e) {
1066+
* if (e.isInstanceOf[IOException]) { ... }
1067+
* else if (e.isInstanceOf[Exception]) { ... }
1068+
* else {
1069+
* throw e; // default, re-throw
1070+
* }
1071+
* }
1072+
* ```
1073+
*
1074+
* In addition, there are provisions to handle catching JavaScript
1075+
* exceptions (which do not extend `Throwable`) as wrapped in a
1076+
* `js.JavaScriptException`.
1077+
*/
1078+
private def genTry(tree: Try, isStat: Boolean): js.Tree = {
1079+
implicit val pos: SourcePosition = tree.sourcePos
1080+
val Try(block, catches, finalizer) = tree
1081+
1082+
val blockAST = genStatOrExpr(block, isStat)
1083+
val resultType = toIRType(tree.tpe)
1084+
1085+
val handled =
1086+
if (catches.isEmpty) blockAST
1087+
else genTryCatch(blockAST, catches, resultType, isStat)
1088+
1089+
genStat(finalizer) match {
1090+
case js.Skip() => handled
1091+
case ast => js.TryFinally(handled, ast)
1092+
}
1093+
}
1094+
1095+
private def genTryCatch(body: js.Tree, catches: List[CaseDef],
1096+
resultType: jstpe.Type,
1097+
isStat: Boolean)(implicit pos: SourcePosition): js.Tree = {
1098+
val exceptIdent = freshLocalIdent("e")
1099+
val origExceptVar = js.VarRef(exceptIdent)(jstpe.AnyType)
1100+
1101+
val mightCatchJavaScriptException = catches.exists { caseDef =>
1102+
caseDef.pat match {
1103+
case Typed(Ident(nme.WILDCARD), tpt) =>
1104+
isMaybeJavaScriptException(tpt.tpe)
1105+
case Ident(nme.WILDCARD) =>
1106+
true
1107+
case pat @ Bind(_, _) =>
1108+
isMaybeJavaScriptException(pat.symbol.info)
1109+
}
1110+
}
1111+
1112+
val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) {
1113+
val valDef = js.VarDef(freshLocalIdent("e"),
1114+
encodeClassType(defn.ThrowableClass), mutable = false, {
1115+
genModuleApplyMethod(jsdefn.Runtime_wrapJavaScriptException, origExceptVar :: Nil)
1116+
})
1117+
(valDef, valDef.ref)
1118+
} else {
1119+
(js.Skip(), origExceptVar)
1120+
}
1121+
1122+
val elseHandler: js.Tree = js.Throw(origExceptVar)
1123+
1124+
val handler = catches.foldRight(elseHandler) { (caseDef, elsep) =>
1125+
implicit val pos: SourcePosition = caseDef.sourcePos
1126+
val CaseDef(pat, _, body) = caseDef
1127+
1128+
// Extract exception type and variable
1129+
val (tpe, boundVar) = (pat match {
1130+
case Typed(Ident(nme.WILDCARD), tpt) =>
1131+
(tpt.tpe, None)
1132+
case Ident(nme.WILDCARD) =>
1133+
(defn.ThrowableType, None)
1134+
case Bind(_, _) =>
1135+
(pat.symbol.info, Some(encodeLocalSym(pat.symbol)))
1136+
})
1137+
1138+
// Generate the body that must be executed if the exception matches
1139+
val bodyWithBoundVar = (boundVar match {
1140+
case None =>
1141+
genStatOrExpr(body, isStat)
1142+
case Some(bv) =>
1143+
val castException = genAsInstanceOf(exceptVar, tpe)
1144+
js.Block(
1145+
js.VarDef(bv, toIRType(tpe), mutable = false, castException),
1146+
genStatOrExpr(body, isStat))
1147+
})
1148+
1149+
// Generate the test
1150+
if (tpe =:= defn.ThrowableType) {
1151+
bodyWithBoundVar
1152+
} else {
1153+
val cond = genIsInstanceOf(exceptVar, tpe)
1154+
js.If(cond, bodyWithBoundVar, elsep)(resultType)
1155+
}
1156+
}
1157+
1158+
js.TryCatch(body, exceptIdent,
1159+
js.Block(exceptValDef, handler))(resultType)
1160+
}
1161+
10541162
/** Gen JS code for an Apply node (method call)
10551163
*
10561164
* There's a whole bunch of varieties of Apply nodes: regular method
@@ -1915,7 +2023,7 @@ class JSCodeGen()(implicit ctx: Context) {
19152023
* primitive instead.)
19162024
*/
19172025
private def genTypeApply(tree: TypeApply): js.Tree = {
1918-
implicit val pos = tree.span
2026+
implicit val pos: SourcePosition = tree.sourcePos
19192027

19202028
val TypeApply(fun, targs) = tree
19212029

@@ -1934,7 +2042,7 @@ class JSCodeGen()(implicit ctx: Context) {
19342042
if (sym == defn.Any_asInstanceOf) {
19352043
genAsInstanceOf(genReceiver, to)
19362044
} else if (sym == defn.Any_isInstanceOf) {
1937-
genIsInstanceOf(tree, genReceiver, to)
2045+
genIsInstanceOf(genReceiver, to)
19382046
} else {
19392047
throw new FatalError(
19402048
s"Unexpected type application $fun with symbol ${sym.fullName}")
@@ -2131,8 +2239,8 @@ class JSCodeGen()(implicit ctx: Context) {
21312239
}
21322240

21332241
/** Gen JS code for an isInstanceOf test (for reference types only) */
2134-
private def genIsInstanceOf(tree: Tree, value: js.Tree, to: Type): js.Tree = {
2135-
implicit val pos = tree.span
2242+
private def genIsInstanceOf(value: js.Tree, to: Type)(
2243+
implicit pos: SourcePosition): js.Tree = {
21362244
val sym = to.widenDealias.typeSymbol
21372245

21382246
if (sym == defn.ObjectClass) {
@@ -2141,7 +2249,7 @@ class JSCodeGen()(implicit ctx: Context) {
21412249
if (sym.is(Trait)) {
21422250
ctx.error(
21432251
s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
2144-
tree.sourcePos)
2252+
pos)
21452253
js.BooleanLiteral(true)
21462254
} else {
21472255
js.Unbox(js.JSBinaryOp(
@@ -2804,6 +2912,9 @@ class JSCodeGen()(implicit ctx: Context) {
28042912
)
28052913
}
28062914

2915+
private def isMaybeJavaScriptException(tpe: Type): Boolean =
2916+
jsdefn.JavaScriptExceptionClass.isSubClass(tpe.typeSymbol)
2917+
28072918
// Copied from DottyBackendInterface
28082919

28092920
private val desugared = new java.util.IdentityHashMap[Type, tpd.Select]

0 commit comments

Comments
 (0)