summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-09-18 10:26:01 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2015-09-18 10:32:27 +0200
commit59a5d3b8fd036d36afcf6349bc3cc527344981a1 (patch)
treefb20fd522a1b7eb82097dd1d1be454acc95a065f
parentc7112d997452fc6d0e7c69a1fec4928bd6c072ad (diff)
downloadscala-59a5d3b8fd036d36afcf6349bc3cc527344981a1.tar.gz
scala-59a5d3b8fd036d36afcf6349bc3cc527344981a1.tar.bz2
scala-59a5d3b8fd036d36afcf6349bc3cc527344981a1.zip
Emit exception handlers for inlined methods in the correct order
Handler tables are lists of tuples (try-start, try-end, handler-start, exception-type). When an instruction throws, the first handler in the list that covers the instruction and matches the type is executed. For nested handlers, it is the job of the compiler to add them to the handler table in the correct order. When inlining a method, the handlers of the callee are prepended to the list of handlers in the callsite method. This ensures that the callee's handlers are tested first if an exception is thrown in the inlined code.
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala6
-rw-r--r--test/files/run/inlineHandlers.scala7
2 files changed, 12 insertions, 1 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index 54f1b99876..20256ca63b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -409,7 +409,11 @@ class Inliner[BT <: BTypes](val btypes: BT) {
callsiteMethod.instructions.remove(callsiteInstruction)
callsiteMethod.localVariables.addAll(cloneLocalVariableNodes(callee, labelsMap, callee.name + "_", localVarShift).asJava)
- callsiteMethod.tryCatchBlocks.addAll(cloneTryCatchBlockNodes(callee, labelsMap).asJava)
+ // prepend the handlers of the callee. the order of handlers matters: when an exception is thrown
+ // at some instruction, the first handler guarding that instruction and having a matching exception
+ // type is executed. prepending the callee's handlers makes sure to test those handlers first if
+ // an exception is thrown in the inlined code.
+ callsiteMethod.tryCatchBlocks.addAll(0, cloneTryCatchBlockNodes(callee, labelsMap).asJava)
callsiteMethod.maxLocals += returnType.getSize + callee.maxLocals
val maxStackOfInlinedCode = {
diff --git a/test/files/run/inlineHandlers.scala b/test/files/run/inlineHandlers.scala
new file mode 100644
index 0000000000..8c672a07b9
--- /dev/null
+++ b/test/files/run/inlineHandlers.scala
@@ -0,0 +1,7 @@
+object Test {
+ @noinline def ham: String = throw null
+ @inline def inner: String = try { ham } catch { case _: NullPointerException => "npe" }
+ def foo = try inner catch { case e: Throwable => throw e }
+
+ def main(args: Array[String]): Unit = assert(foo == "npe")
+}