Skip to content

Commit 4a16b80

Browse files
committed
Scala.js: Handle EmptyTree and Try in the back-end.
1 parent 450c08e commit 4a16b80

File tree

1 file changed

+120
-9
lines changed

1 file changed

+120
-9
lines changed

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

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,8 @@ class JSCodeGen()(implicit ctx: Context) {
861861
case WhileDo(cond, body) =>
862862
js.While(genExpr(cond), genStat(body))
863863

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

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

1016-
/*case EmptyTree =>
1017-
js.Skip()*/
1016+
case EmptyTree =>
1017+
js.Skip()
10181018

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

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

19212029
val TypeApply(fun, targs) = tree
19222030

@@ -1935,7 +2043,7 @@ class JSCodeGen()(implicit ctx: Context) {
19352043
if (sym == defn.Any_asInstanceOf) {
19362044
genAsInstanceOf(genReceiver, to)
19372045
} else if (sym == defn.Any_isInstanceOf) {
1938-
genIsInstanceOf(tree, genReceiver, to)
2046+
genIsInstanceOf(genReceiver, to)
19392047
} else {
19402048
throw new FatalError(
19412049
s"Unexpected type application $fun with symbol ${sym.fullName}")
@@ -2132,8 +2240,8 @@ class JSCodeGen()(implicit ctx: Context) {
21322240
}
21332241

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

21392247
if (sym == defn.ObjectClass) {
@@ -2142,7 +2250,7 @@ class JSCodeGen()(implicit ctx: Context) {
21422250
if (sym.is(Trait)) {
21432251
ctx.error(
21442252
s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
2145-
tree.sourcePos)
2253+
pos)
21462254
js.BooleanLiteral(true)
21472255
} else {
21482256
js.Unbox(js.JSBinaryOp(
@@ -2805,6 +2913,9 @@ class JSCodeGen()(implicit ctx: Context) {
28052913
)
28062914
}
28072915

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

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

0 commit comments

Comments
 (0)