Skip to content

Commit 33ed758

Browse files
authored
Merge pull request scala-js#4697 from sjrd/ir-nodes-wrap-unwrap-js-exception
Introduce IR nodes to wrap/unwrap JavaScript exceptions.
2 parents d5b1fa8 + 994a598 commit 33ed758

File tree

34 files changed

+671
-76
lines changed

34 files changed

+671
-76
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,14 +2398,10 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
23982398
case Throw(expr) =>
23992399
val ex = genExpr(expr)
24002400
js.Throw {
2401-
if (!ex.isInstanceOf[js.Null] && isMaybeJavaScriptException(expr.tpe)) {
2402-
genApplyMethod(
2403-
genLoadModule(RuntimePackageModule),
2404-
Runtime_unwrapJavaScriptException,
2405-
List(ex))
2406-
} else {
2401+
if (!ex.isInstanceOf[js.Null] && isMaybeJavaScriptException(expr.tpe))
2402+
js.UnwrapFromThrowable(ex)
2403+
else
24072404
ex
2408-
}
24092405
}
24102406

24112407
/* !!! Copy-pasted from `CleanUp.scala` upstream and simplified with
@@ -2930,12 +2926,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
29302926

29312927
val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) {
29322928
val valDef = js.VarDef(freshLocalIdent("e"), NoOriginalName,
2933-
encodeClassType(ThrowableClass), mutable = false, {
2934-
genApplyMethod(
2935-
genLoadModule(RuntimePackageModule),
2936-
Runtime_wrapJavaScriptException,
2937-
List(origExceptVar))
2938-
})
2929+
encodeClassType(ThrowableClass), mutable = false, js.WrapAsThrowable(origExceptVar))
29392930
(valDef, valDef.ref)
29402931
} else {
29412932
(js.Skip(), origExceptVar)
@@ -5297,6 +5288,53 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
52975288
js.ForIn(objVarDef.ref, keyVarIdent, NoOriginalName, {
52985289
js.JSFunctionApply(fVarDef.ref, List(keyVarRef))
52995290
}))
5291+
5292+
case JS_THROW =>
5293+
// js.special.throw(arg)
5294+
js.Throw(genArgs1)
5295+
5296+
case JS_TRY_CATCH =>
5297+
/* js.special.tryCatch(arg1, arg2)
5298+
*
5299+
* We must generate:
5300+
*
5301+
* val body = arg1
5302+
* val handler = arg2
5303+
* try {
5304+
* body()
5305+
* } catch (e) {
5306+
* handler(e)
5307+
* }
5308+
*
5309+
* with temporary vals, because `arg2` must be evaluated before
5310+
* `body` executes. Moreover, exceptions thrown while evaluating
5311+
* the function values `arg1` and `arg2` must not be caught.
5312+
*/
5313+
val (arg1, arg2) = genArgs2
5314+
val bodyVarDef = js.VarDef(freshLocalIdent("body"), NoOriginalName,
5315+
jstpe.AnyType, mutable = false, arg1)
5316+
val handlerVarDef = js.VarDef(freshLocalIdent("handler"), NoOriginalName,
5317+
jstpe.AnyType, mutable = false, arg2)
5318+
val exceptionVarIdent = freshLocalIdent("e")
5319+
val exceptionVarRef = js.VarRef(exceptionVarIdent)(jstpe.AnyType)
5320+
js.Block(
5321+
bodyVarDef,
5322+
handlerVarDef,
5323+
js.TryCatch(
5324+
js.JSFunctionApply(bodyVarDef.ref, Nil),
5325+
exceptionVarIdent,
5326+
NoOriginalName,
5327+
js.JSFunctionApply(handlerVarDef.ref, List(exceptionVarRef))
5328+
)(jstpe.AnyType)
5329+
)
5330+
5331+
case WRAP_AS_THROWABLE =>
5332+
// js.special.wrapAsThrowable(arg)
5333+
js.WrapAsThrowable(genArgs1)
5334+
5335+
case UNWRAP_FROM_THROWABLE =>
5336+
// js.special.unwrapFromThrowable(arg)
5337+
js.UnwrapFromThrowable(genArgs1)
53005338
}
53015339
}
53025340

compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,13 @@ trait JSDefinitions {
107107
lazy val Special_instanceof = getMemberMethod(SpecialPackageModule, newTermName("instanceof"))
108108
lazy val Special_delete = getMemberMethod(SpecialPackageModule, newTermName("delete"))
109109
lazy val Special_forin = getMemberMethod(SpecialPackageModule, newTermName("forin"))
110+
lazy val Special_throw = getMemberMethod(SpecialPackageModule, newTermName("throw"))
111+
lazy val Special_tryCatch = getMemberMethod(SpecialPackageModule, newTermName("tryCatch"))
112+
lazy val Special_wrapAsThrowable = getMemberMethod(SpecialPackageModule, newTermName("wrapAsThrowable"))
113+
lazy val Special_unwrapFromThrowable = getMemberMethod(SpecialPackageModule, newTermName("unwrapFromThrowable"))
110114
lazy val Special_debugger = getMemberMethod(SpecialPackageModule, newTermName("debugger"))
111115

112116
lazy val RuntimePackageModule = getPackageObject("scala.scalajs.runtime")
113-
lazy val Runtime_wrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("wrapJavaScriptException"))
114-
lazy val Runtime_unwrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("unwrapJavaScriptException"))
115117
lazy val Runtime_toScalaVarArgs = getMemberMethod(RuntimePackageModule, newTermName("toScalaVarArgs"))
116118
lazy val Runtime_toJSVarArgs = getMemberMethod(RuntimePackageModule, newTermName("toJSVarArgs"))
117119
lazy val Runtime_constructorOf = getMemberMethod(RuntimePackageModule, newTermName("constructorOf"))

compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,16 @@ abstract class JSPrimitives {
5959
final val IDENTITY_HASH_CODE = LINKING_INFO + 1 // runtime.identityHashCode
6060
final val DYNAMIC_IMPORT = IDENTITY_HASH_CODE + 1 // runtime.dynamicImport
6161

62-
final val STRICT_EQ = DYNAMIC_IMPORT + 1 // js.special.strictEquals
63-
final val IN = STRICT_EQ + 1 // js.special.in
64-
final val INSTANCEOF = IN + 1 // js.special.instanceof
65-
final val DELETE = INSTANCEOF + 1 // js.special.delete
66-
final val FORIN = DELETE + 1 // js.special.forin
67-
final val DEBUGGER = FORIN + 1 // js.special.debugger
62+
final val STRICT_EQ = DYNAMIC_IMPORT + 1 // js.special.strictEquals
63+
final val IN = STRICT_EQ + 1 // js.special.in
64+
final val INSTANCEOF = IN + 1 // js.special.instanceof
65+
final val DELETE = INSTANCEOF + 1 // js.special.delete
66+
final val FORIN = DELETE + 1 // js.special.forin
67+
final val JS_THROW = FORIN + 1 // js.special.throw
68+
final val JS_TRY_CATCH = JS_THROW + 1 // js.special.tryCatch
69+
final val WRAP_AS_THROWABLE = JS_TRY_CATCH + 1 // js.special.wrapAsThrowable
70+
final val UNWRAP_FROM_THROWABLE = WRAP_AS_THROWABLE + 1 // js.special.unwrapFromThrowable
71+
final val DEBUGGER = UNWRAP_FROM_THROWABLE + 1 // js.special.debugger
6872

6973
final val LastJSPrimitiveCode = DEBUGGER
7074

@@ -114,6 +118,10 @@ abstract class JSPrimitives {
114118
addPrimitive(Special_instanceof, INSTANCEOF)
115119
addPrimitive(Special_delete, DELETE)
116120
addPrimitive(Special_forin, FORIN)
121+
addPrimitive(Special_throw, JS_THROW)
122+
addPrimitive(Special_tryCatch, JS_TRY_CATCH)
123+
addPrimitive(Special_wrapAsThrowable, WRAP_AS_THROWABLE)
124+
addPrimitive(Special_unwrapFromThrowable, UNWRAP_FROM_THROWABLE)
117125
addPrimitive(Special_debugger, DEBUGGER)
118126
}
119127

compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,8 @@ class OptimizationTest extends JSASTTest {
517517
}
518518
}
519519
}
520-
""".hasNot("call to the scala.scalajs.runtime.package$ package module class") {
521-
case js.LoadModule(ScalaJSRuntimePackageClass) =>
520+
""".hasNot("WrapAsThrowable") {
521+
case js.WrapAsThrowable(_) =>
522522
}
523523

524524
// Confidence check
@@ -534,16 +534,15 @@ class OptimizationTest extends JSASTTest {
534534
}
535535
}
536536
}
537-
""".hasExactly(1, "call to the scala.scalajs.runtime.package$ package module class") {
538-
case js.LoadModule(ScalaJSRuntimePackageClass) =>
537+
""".hasExactly(1, "WrapAsThrowable") {
538+
case js.WrapAsThrowable(_) =>
539539
}
540540
}
541541
}
542542

543543
object OptimizationTest {
544544

545545
private val ArrayModuleClass = ClassName("scala.Array$")
546-
private val ScalaJSRuntimePackageClass = ClassName("scala.scalajs.runtime.package$")
547546

548547
private val applySimpleMethodName = SimpleMethodName("apply")
549548

ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ object Hashers {
345345
mixTag(TagIdentityHashCode)
346346
mixTree(expr)
347347

348+
case WrapAsThrowable(expr) =>
349+
mixTag(TagWrapAsThrowable)
350+
mixTree(expr)
351+
352+
case UnwrapFromThrowable(expr) =>
353+
mixTag(TagUnwrapFromThrowable)
354+
mixTree(expr)
355+
348356
case JSNew(ctor, args) =>
349357
mixTag(TagJSNew)
350358
mixTree(ctor)

ir/shared/src/main/scala/org/scalajs/ir/Names.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,13 @@ object Names {
486486
/** `java.io.Serializable`, which is an ancestor of array classes. */
487487
val SerializableClass: ClassName = ClassName("java.io.Serializable")
488488

489+
/** The superclass of all throwables.
490+
*
491+
* This is the result type of `WrapAsThrowable` nodes, as well as the input
492+
* type of `UnwrapFromThrowable`.
493+
*/
494+
val ThrowableClass = ClassName("java.lang.Throwable")
495+
489496
/** The exception thrown by a division by 0. */
490497
val ArithmeticExceptionClass: ClassName =
491498
ClassName("java.lang.ArithmeticException")

ir/shared/src/main/scala/org/scalajs/ir/Printers.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,16 @@ object Printers {
558558
print(expr)
559559
print(')')
560560

561+
case WrapAsThrowable(expr) =>
562+
print("<wrapAsThrowable>(")
563+
print(expr)
564+
print(")")
565+
566+
case UnwrapFromThrowable(expr) =>
567+
print("<unwrapFromThrowable>(")
568+
print(expr)
569+
print(")")
570+
561571
// JavaScript expressions
562572

563573
case JSNew(ctor, args) =>

ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import scala.util.matching.Regex
1818

1919
object ScalaJSVersions extends VersionChecks(
2020
current = "1.11.0-SNAPSHOT",
21-
binaryEmitted = "1.8"
21+
binaryEmitted = "1.11-SNAPSHOT"
2222
)
2323

2424
/** Helper class to allow for testing of logic. */

ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,14 @@ object Serializers {
412412
writeTagAndPos(TagIdentityHashCode)
413413
writeTree(expr)
414414

415+
case WrapAsThrowable(expr) =>
416+
writeTagAndPos(TagWrapAsThrowable)
417+
writeTree(expr)
418+
419+
case UnwrapFromThrowable(expr) =>
420+
writeTagAndPos(TagUnwrapFromThrowable)
421+
writeTree(expr)
422+
415423
case JSNew(ctor, args) =>
416424
writeTagAndPos(TagJSNew)
417425
writeTree(ctor); writeTreeOrJSSpreads(args)
@@ -1142,6 +1150,11 @@ object Serializers {
11421150
case TagClone => Clone(readTree())
11431151
case TagIdentityHashCode => IdentityHashCode(readTree())
11441152

1153+
case TagWrapAsThrowable =>
1154+
WrapAsThrowable(readTree())
1155+
case TagUnwrapFromThrowable =>
1156+
UnwrapFromThrowable(readTree())
1157+
11451158
case TagJSNew => JSNew(readTree(), readTreeOrJSSpreads())
11461159
case TagJSPrivateSelect => JSPrivateSelect(readTree(), readClassName(), readFieldIdent())
11471160
case TagJSSelect => JSSelect(readTree(), readTree())

ir/shared/src/main/scala/org/scalajs/ir/Tags.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ private[ir] object Tags {
122122

123123
final val TagJSNewTarget = TagJSImportMeta + 1
124124

125+
// New in 1.11
126+
127+
final val TagWrapAsThrowable = TagJSNewTarget + 1
128+
final val TagUnwrapFromThrowable = TagWrapAsThrowable + 1
129+
125130
// Tags for member defs
126131

127132
final val TagFieldDef = 1

ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ object Transformers {
149149
case IdentityHashCode(expr) =>
150150
IdentityHashCode(transformExpr(expr))
151151

152+
case WrapAsThrowable(expr) =>
153+
WrapAsThrowable(transformExpr(expr))
154+
155+
case UnwrapFromThrowable(expr) =>
156+
UnwrapFromThrowable(transformExpr(expr))
157+
152158
// JavaScript expressions
153159

154160
case JSNew(ctor, args) =>

ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ object Traversers {
142142
case IdentityHashCode(expr) =>
143143
traverse(expr)
144144

145+
case WrapAsThrowable(expr) =>
146+
traverse(expr)
147+
148+
case UnwrapFromThrowable(expr) =>
149+
traverse(expr)
150+
145151
// JavaScript expressions
146152

147153
case JSNew(ctor, args) =>

ir/shared/src/main/scala/org/scalajs/ir/Trees.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,16 @@ object Trees {
493493
val tpe = IntType
494494
}
495495

496+
sealed case class WrapAsThrowable(expr: Tree)(implicit val pos: Position)
497+
extends Tree {
498+
val tpe = ClassType(ThrowableClass)
499+
}
500+
501+
sealed case class UnwrapFromThrowable(expr: Tree)(implicit val pos: Position)
502+
extends Tree {
503+
val tpe = AnyType
504+
}
505+
496506
// JavaScript expressions
497507

498508
sealed case class JSNew(ctor: Tree, args: List[TreeOrJSSpread])(

ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,15 @@ class PrintersTest {
599599
assertPrintEquals("<identityHashCode>(x)", IdentityHashCode(ref("x", AnyType)))
600600
}
601601

602+
@Test def printWrapAsThrowable(): Unit = {
603+
assertPrintEquals("<wrapAsThrowable>(e)", WrapAsThrowable(ref("e", AnyType)))
604+
}
605+
606+
@Test def printUnwrapFromThrowable(): Unit = {
607+
assertPrintEquals("<unwrapFromThrowable>(e)",
608+
UnwrapFromThrowable(ref("e", ClassType(ThrowableClass))))
609+
}
610+
602611
@Test def printJSNew(): Unit = {
603612
assertPrintEquals("new C()", JSNew(ref("C", AnyType), Nil))
604613
assertPrintEquals("new C(4, 5)", JSNew(ref("C", AnyType), List(i(4), i(5))))

library/src/main/scala-new-collections/scala/scalajs/js/JSConverters.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,7 @@ object JSConverters extends JSConvertersLowPrioImplicits {
172172
resolve(value)
173173

174174
case scala.util.Failure(th) =>
175-
reject(th match {
176-
case js.JavaScriptException(e) => e
177-
case _ => th
178-
})
175+
reject(js.special.unwrapFromThrowable(th))
179176
}
180177
})
181178
}

library/src/main/scala-old-collections/scala/scalajs/js/JSConverters.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,7 @@ object JSConverters extends js.JSConvertersLowPrioImplicits {
182182
resolve(value)
183183

184184
case scala.util.Failure(th) =>
185-
reject(th match {
186-
case js.JavaScriptException(e) => e
187-
case _ => th
188-
})
185+
reject(js.special.unwrapFromThrowable(th))
189186
}
190187
})
191188
}

library/src/main/scala/scala/scalajs/js/Thenable.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ object Thenable {
5959
(): Unit | js.Thenable[Unit]
6060
},
6161
js.defined { (e: scala.Any) =>
62-
p2.failure(e match {
63-
case th: Throwable => th
64-
case _ => js.JavaScriptException(e)
65-
})
62+
p2.failure(js.special.wrapAsThrowable(e))
6663
(): Unit | js.Thenable[Unit]
6764
})
6865
p2.future

0 commit comments

Comments
 (0)