summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-01-25 14:16:27 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-01-31 11:00:43 -0800
commit71ea3e8278aad030cbe8c9093fe49790a4e419cb (patch)
tree49d19f3ed008ade9245545a3991b244859249bd6
parent62b37dd9a87afd17a67752c6c1b174987817b3e9 (diff)
downloadscala-71ea3e8278aad030cbe8c9093fe49790a4e419cb.tar.gz
scala-71ea3e8278aad030cbe8c9093fe49790a4e419cb.tar.bz2
scala-71ea3e8278aad030cbe8c9093fe49790a4e419cb.zip
no null check for type-tested unapply arg
pattern matching on case classes where pattern is not known to be a subclass of the unapply's argument type used to result in code like: ``` if (x1.isInstanceOf[Foo]) { val x2 = x1.asInstanceOf[Foo] if (x2 != null) { // redundant ... } } ``` this wastes byte code on the redundant null check with this patch, when previous type tests imply the variable cannot be null, there's no null check
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala35
-rw-r--r--test/files/jvm/patmat_opt_no_nullcheck.check1
-rw-r--r--test/files/jvm/patmat_opt_no_nullcheck.flags1
-rw-r--r--test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala25
-rw-r--r--test/files/jvm/patmat_opt_no_nullcheck/test.scala8
-rw-r--r--test/files/run/inline-ex-handlers.check206
6 files changed, 167 insertions, 109 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
index 9b2898741e..f32ca4bd8e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -409,6 +409,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
// example check: List[Int] <:< ::[Int]
// TODO: extractor.paramType may contain unbound type params (run/t2800, run/t3530)
+ // `patBinderOrCasted` is assigned the result of casting `patBinder` to `extractor.paramType`
val (typeTestTreeMaker, patBinderOrCasted, binderKnownNonNull) =
if (needsTypeTest(patBinder.info.widen, extractor.paramType)) {
// chain a type-testing extractor before the actual extractor call
@@ -416,7 +417,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
// TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC]
// (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder)
val treeMaker = TypeTestTreeMaker(patBinder, patBinder, extractor.paramType, extractor.paramType)(pos, extractorArgTypeTest = true)
- (List(treeMaker), treeMaker.nextBinder, false)
+
+ // check whether typetest implies patBinder is not null,
+ // even though the eventual null check will be on patBinderOrCasted
+ // it'll be equal to patBinder casted to extractor.paramType anyway (and the type test is on patBinder)
+ (List(treeMaker), treeMaker.nextBinder, treeMaker.impliesBinderNonNull(patBinder))
} else {
// no type test needed, but the tree maker relies on `patBinderOrCasted` having type `extractor.paramType` (and not just some type compatible with it)
// SI-6624 shows this is necessary because apparently patBinder may have an unfortunate type (.decls don't have the case field accessors)
@@ -773,11 +778,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
def resultType = tpe.finalResultType
def isSeq = extractorCall.symbol.name == nme.unapplySeq
- /** Create the TreeMaker that embodies this extractor call
- *
- * `binder` has been casted to `paramType` if necessary
- * `binderKnownNonNull` is not used in this subclass
- */
+ /** Create the TreeMaker that embodies this extractor call
+ *
+ * `binder` has been casted to `paramType` if necessary
+ * `binderKnownNonNull` is not used in this subclass
+ *
+ * TODO: implement review feedback by @retronym:
+ * Passing the pair of values around suggests:
+ * case class Binder(sym: Symbol, knownNotNull: Boolean).
+ * Perhaps it hasn't reached critical mass, but it would already clean things up a touch.
+ */
def treeMaker(patBinderOrCasted: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = {
// the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern)
val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted))
@@ -1177,6 +1187,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
def eqTest(pat: Tree, testedBinder: Symbol): Result = false
def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false
}
+
+ def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy {
+ type Result = Boolean
+
+ def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder
+ def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false
+ def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder
+ def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null
+ def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null
+ def and(a: Result, b: Result): Result = a || b
+ }
}
/** implements the run-time aspects of (ยง8.2) (typedPattern has already done the necessary type transformations)
@@ -1260,6 +1281,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
// is this purely a type test, e.g. no outer check, no equality tests (used in switch emission)
def isPureTypeTest = renderCondition(pureTypeTestChecker)
+ def impliesBinderNonNull(binder: Symbol) = renderCondition(nonNullImpliedByTestChecker(binder))
+
override def toString = "TT"+(expectedTp, testedBinder.name, nextBinderTp)
}
diff --git a/test/files/jvm/patmat_opt_no_nullcheck.check b/test/files/jvm/patmat_opt_no_nullcheck.check
new file mode 100644
index 0000000000..43f53aba12
--- /dev/null
+++ b/test/files/jvm/patmat_opt_no_nullcheck.check
@@ -0,0 +1 @@
+bytecode identical
diff --git a/test/files/jvm/patmat_opt_no_nullcheck.flags b/test/files/jvm/patmat_opt_no_nullcheck.flags
new file mode 100644
index 0000000000..1182725e86
--- /dev/null
+++ b/test/files/jvm/patmat_opt_no_nullcheck.flags
@@ -0,0 +1 @@
+-optimize \ No newline at end of file
diff --git a/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala
new file mode 100644
index 0000000000..1594eb523c
--- /dev/null
+++ b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala
@@ -0,0 +1,25 @@
+// this class's bytecode, compiled under -optimize is analyzed by the test
+// method a's bytecode should be identical to method b's bytecode
+case class Foo(x: Any)
+
+class SameBytecode {
+ def a =
+ (Foo(1): Any) match {
+ case Foo(_: String) =>
+ }
+
+ // there's no null check
+ def b: Unit = {
+ val x1: Any = Foo(1)
+ if (x1.isInstanceOf[Foo]) {
+ val x3 = x1.asInstanceOf[Foo]
+ if (x3.x.isInstanceOf[String]) {
+ val x4 = x3.x.asInstanceOf[String]
+ val x = ()
+ return
+ }
+ }
+
+ throw new MatchError(x1)
+ }
+} \ No newline at end of file
diff --git a/test/files/jvm/patmat_opt_no_nullcheck/test.scala b/test/files/jvm/patmat_opt_no_nullcheck/test.scala
new file mode 100644
index 0000000000..2927e763d5
--- /dev/null
+++ b/test/files/jvm/patmat_opt_no_nullcheck/test.scala
@@ -0,0 +1,8 @@
+import scala.tools.partest.BytecodeTest
+
+object Test extends BytecodeTest {
+ def show: Unit = {
+ val classNode = loadClassNode("SameBytecode")
+ sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"))
+ }
+}
diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check
index 282542a732..905dfc3ee7 100644
--- a/test/files/run/inline-ex-handlers.check
+++ b/test/files/run/inline-ex-handlers.check
@@ -26,54 +26,55 @@
---
> locals: value args, variable result, value ex6, value x4, value x5, value x
397c393
-< blocks: [1,2,3,4,5,8,11,13,14,16]
+< blocks: [1,2,3,4,5,8,10,11,13]
---
-> blocks: [1,2,3,5,8,11,13,14,16,17]
+> blocks: [1,2,3,5,8,10,11,13,14]
421c417,426
< 103 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 17
+> ? JUMP 14
>
-> 17:
+> 14:
> 101 LOAD_LOCAL(value ex6)
> 101 STORE_LOCAL(value x4)
> 101 SCOPE_ENTER value x4
> 106 LOAD_LOCAL(value x4)
> 106 IS_INSTANCE REF(class MyException)
-> 106 CZJUMP (BOOL)NE ? 5 : 11
+> 106 CZJUMP (BOOL)NE ? 5 : 8
434,436d438
< 101 JUMP 4
<
< 4:
-450,453d451
+446,449d447
< 106 LOAD_LOCAL(value x5)
< 106 CALL_METHOD MyException.message (dynamic)
< 106 STORE_LOCAL(value message)
< 106 SCOPE_ENTER value message
-455c453,454
+451c449,450
< 106 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 106 CALL_METHOD MyException.message (dynamic)
-527c526
+523c522
< blocks: [1,2,3,4,6,7,8,9,10]
---
> blocks: [1,2,3,4,6,7,8,9,10,11,12,13]
-556c555,560
+552c551
< 306 THROW(MyException)
---
> ? JUMP 11
->
+553a553,557
> 11:
> ? LOAD_LOCAL(variable monitor4)
> 305 MONITOR_EXIT
> ? JUMP 12
-562c566
+>
+558c562
< ? THROW(Throwable)
---
> ? JUMP 12
-568c572,579
+564c568,575
< ? THROW(Throwable)
---
> ? STORE_LOCAL(value t)
@@ -84,7 +85,7 @@
> 304 MONITOR_EXIT
> ? STORE_LOCAL(value t)
> ? JUMP 13
-583a595,606
+579a591,602
> 13:
> 310 LOAD_MODULE object Predef
> 310 CALL_PRIMITIVE(StartConcat)
@@ -97,35 +98,35 @@
> 310 CALL_METHOD scala.Predef.println (dynamic)
> 310 JUMP 2
>
-592c615
+588c611
< catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6
---
> catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6
-595c618
+591c614
< catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3
---
> catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3
-627c650
+623c646
< blocks: [1,2,3,4,5,6,7,9,10]
---
> blocks: [1,2,3,4,5,6,7,9,10,11,12]
-651c674,675
+647c670,671
< 78 THROW(IllegalArgumentException)
---
> ? STORE_LOCAL(value e)
> ? JUMP 11
-652a677,681
+648a673,677
> 11:
> 81 LOAD_LOCAL(value e)
> ? STORE_LOCAL(variable exc1)
> ? JUMP 12
>
-680c709,710
+676c705,706
< 81 THROW(Exception)
---
> ? STORE_LOCAL(variable exc1)
> ? JUMP 12
-696a727,739
+692a723,735
> 12:
> 83 LOAD_MODULE object Predef
> 83 CONSTANT("finally")
@@ -139,88 +140,88 @@
> 84 LOAD_LOCAL(variable exc1)
> 84 THROW(Throwable)
>
-702c745
+698c741
< catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3
---
> catch (<none>) in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3
-726c769
+722c765
< locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x
---
> locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x
-728c771
-< blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31]
+724c767
+< blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25]
---
-> blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31,32,33,34]
-752c795,802
+> blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25,26,27,28]
+748c791,798
< 172 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 32
+> ? JUMP 26
>
-> 32:
+> 26:
> 170 LOAD_LOCAL(value ex6)
> 170 STORE_LOCAL(value x4)
> 170 SCOPE_ENTER value x4
-> 170 JUMP 18
-799,802d848
+> 170 JUMP 15
+791,794d840
< 175 LOAD_LOCAL(value x5)
< 175 CALL_METHOD MyException.message (dynamic)
< 175 STORE_LOCAL(value message)
< 175 SCOPE_ENTER value message
-804c850,851
+796c842,843
< 176 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 176 CALL_METHOD MyException.message (dynamic)
-808c855,856
+800c847,848
< 177 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 177 CALL_METHOD MyException.message (dynamic)
-810c858,859
+802c850,851
< 177 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 33
-814c863,864
+> ? JUMP 27
+806c855,856
< 170 THROW(Throwable)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 33
-823a874,879
-> 33:
+> ? JUMP 27
+815a866,871
+> 27:
> 169 LOAD_LOCAL(value ex6)
> 169 STORE_LOCAL(value x4)
> 169 SCOPE_ENTER value x4
> 169 JUMP 5
>
-838,841d893
+826,829d881
< 180 LOAD_LOCAL(value x5)
< 180 CALL_METHOD MyException.message (dynamic)
< 180 STORE_LOCAL(value message)
< 180 SCOPE_ENTER value message
-843c895,896
+831c883,884
< 181 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 181 CALL_METHOD MyException.message (dynamic)
-847c900,901
+835c888,889
< 182 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 182 CALL_METHOD MyException.message (dynamic)
-849c903,904
+837c891,892
< 182 THROW(MyException)
---
> ? STORE_LOCAL(variable exc2)
-> ? JUMP 34
-853c908,909
+> ? JUMP 28
+841c896,897
< 169 THROW(Throwable)
---
> ? STORE_LOCAL(variable exc2)
-> ? JUMP 34
-869a926,938
-> 34:
+> ? JUMP 28
+857a914,926
+> 28:
> 184 LOAD_MODULE object Predef
> 184 CONSTANT("finally")
> 184 CALL_METHOD scala.Predef.println (dynamic)
@@ -233,159 +234,158 @@
> 185 LOAD_LOCAL(variable exc2)
> 185 THROW(Throwable)
>
-875c944
-< catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30) starting at: 4
+863c932
+< catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24) starting at: 4
---
-> catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30, 32) starting at: 4
-878c947
-< catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30) starting at: 3
+> catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24, 26) starting at: 4
+866c935
+< catch (<none>) in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24) starting at: 3
---
-> catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30, 32, 33) starting at: 3
-902c971
+> catch (<none>) in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24, 26, 27) starting at: 3
+890c959
< locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x
---
> locals: value args, variable result, value e, value ex6, value x4, value x5, value x
-904c973
-< blocks: [1,2,3,6,7,8,11,14,16,17,19]
+892c961
+< blocks: [1,2,3,6,7,8,11,13,14,16]
---
-> blocks: [1,2,3,6,7,8,11,14,16,17,19,20]
-928c997,1004
+> blocks: [1,2,3,6,7,8,11,13,14,16,17]
+916c985,992
< 124 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 20
+> ? JUMP 17
>
-> 20:
+> 17:
> 122 LOAD_LOCAL(value ex6)
> 122 STORE_LOCAL(value x4)
> 122 SCOPE_ENTER value x4
> 122 JUMP 7
-957,960d1032
+941,944d1016
< 127 LOAD_LOCAL(value x5)
< 127 CALL_METHOD MyException.message (dynamic)
< 127 STORE_LOCAL(value message)
< 127 SCOPE_ENTER value message
-962c1034,1035
+946c1018,1019
< 127 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 127 CALL_METHOD MyException.message (dynamic)
-991c1064
-< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19) starting at: 3
+975c1048
+< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3
---
-> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19, 20) starting at: 3
-1015c1088
+> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3
+999c1072
< locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e
---
> locals: value args, variable result, value ex6, value x4, value x5, value x, value e
-1017c1090
-< blocks: [1,2,3,4,5,8,11,15,16,17,19]
+1001c1074
+< blocks: [1,2,3,4,5,8,12,13,14,16]
---
-> blocks: [1,2,3,5,8,11,15,16,17,19,20]
-1041c1114,1123
+> blocks: [1,2,3,5,8,12,13,14,16,17]
+1025c1098,1107
< 148 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 20
+> ? JUMP 17
>
-> 20:
+> 17:
> 145 LOAD_LOCAL(value ex6)
> 145 STORE_LOCAL(value x4)
> 145 SCOPE_ENTER value x4
> 154 LOAD_LOCAL(value x4)
> 154 IS_INSTANCE REF(class MyException)
-> 154 CZJUMP (BOOL)NE ? 5 : 11
-1062,1064d1143
+> 154 CZJUMP (BOOL)NE ? 5 : 8
+1046,1048d1127
< 145 JUMP 4
<
< 4:
-1078,1081d1156
+1058,1061d1136
< 154 LOAD_LOCAL(value x5)
< 154 CALL_METHOD MyException.message (dynamic)
< 154 STORE_LOCAL(value message)
< 154 SCOPE_ENTER value message
-1083c1158,1159
+1063c1138,1139
< 154 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 154 CALL_METHOD MyException.message (dynamic)
-1300c1376
+1280c1356
< blocks: [1,2,3,4,5,7]
---
> blocks: [1,2,3,4,5,7,8]
-1324c1400,1401
+1304c1380,1387
< 38 THROW(IllegalArgumentException)
---
> ? STORE_LOCAL(value e)
> ? JUMP 8
-1325a1403,1408
+>
> 8:
> 42 LOAD_MODULE object Predef
> 42 CONSTANT("IllegalArgumentException")
> 42 CALL_METHOD scala.Predef.println (dynamic)
> 42 JUMP 2
->
-1371c1454
+1351c1434
< locals: value args, variable result, value ex6, value x4, value x5, value message, value x
---
> locals: value args, variable result, value ex6, value x4, value x5, value x
-1373c1456
-< blocks: [1,2,3,4,5,8,11,13,14,16,17,19]
+1353c1436
+< blocks: [1,2,3,4,5,8,10,11,13,14,16]
---
-> blocks: [1,2,3,5,8,11,13,14,16,17,19,20]
-1397c1480,1481
+> blocks: [1,2,3,5,8,10,11,13,14,16,17]
+1377c1460,1461
< 203 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 20
-1417c1501,1510
+> ? JUMP 17
+1397c1481,1490
< 209 THROW(MyException)
---
> ? STORE_LOCAL(value ex6)
-> ? JUMP 20
+> ? JUMP 17
>
-> 20:
+> 17:
> 200 LOAD_LOCAL(value ex6)
> 200 STORE_LOCAL(value x4)
> 200 SCOPE_ENTER value x4
> 212 LOAD_LOCAL(value x4)
> 212 IS_INSTANCE REF(class MyException)
-> 212 CZJUMP (BOOL)NE ? 5 : 11
-1430,1432d1522
+> 212 CZJUMP (BOOL)NE ? 5 : 8
+1410,1412d1502
< 200 JUMP 4
<
< 4:
-1446,1449d1535
+1422,1425d1511
< 212 LOAD_LOCAL(value x5)
< 212 CALL_METHOD MyException.message (dynamic)
< 212 STORE_LOCAL(value message)
< 212 SCOPE_ENTER value message
-1451c1537,1538
+1427c1513,1514
< 213 LOAD_LOCAL(value message)
---
> ? LOAD_LOCAL(value x5)
> 213 CALL_METHOD MyException.message (dynamic)
-1495c1582
+1471c1558
< blocks: [1,2,3,4,5,7]
---
> blocks: [1,2,3,4,5,7,8]
-1519c1606,1607
+1495c1582,1583
< 58 THROW(IllegalArgumentException)
---
> ? STORE_LOCAL(value e)
> ? JUMP 8
-1520a1609,1614
+1496a1585,1590
> 8:
> 62 LOAD_MODULE object Predef
> 62 CONSTANT("RuntimeException")
> 62 CALL_METHOD scala.Predef.println (dynamic)
> 62 JUMP 2
>
-1568c1662
+1544c1638
< blocks: [1,2,3,4]
---
> blocks: [1,2,3,4,5]
-1588c1682,1687
+1564c1658,1663
< 229 THROW(MyException)
---
> ? JUMP 5
@@ -394,19 +394,19 @@
> ? LOAD_LOCAL(variable monitor1)
> 228 MONITOR_EXIT
> 228 THROW(Throwable)
-1594c1693
+1570c1669
< ? THROW(Throwable)
---
> 228 THROW(Throwable)
-1622c1721
+1598c1697
< locals: value args, variable result, variable monitor2, variable monitorResult1
---
> locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1
-1624c1723
+1600c1699
< blocks: [1,2,3,4]
---
> blocks: [1,2,3,4,5]
-1647c1746,1754
+1623c1722,1730
< 245 THROW(MyException)
---
> ? STORE_LOCAL(value exception$1)
@@ -418,7 +418,7 @@
> ? LOAD_LOCAL(variable monitor2)
> 244 MONITOR_EXIT
> 244 THROW(Throwable)
-1653c1760
+1629c1736
< ? THROW(Throwable)
---
> 244 THROW(Throwable)