diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-09-18 10:26:01 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-09-18 10:32:27 +0200 |
commit | 59a5d3b8fd036d36afcf6349bc3cc527344981a1 (patch) | |
tree | fb20fd522a1b7eb82097dd1d1be454acc95a065f | |
parent | c7112d997452fc6d0e7c69a1fec4928bd6c072ad (diff) | |
download | scala-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.scala | 6 | ||||
-rw-r--r-- | test/files/run/inlineHandlers.scala | 7 |
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") +} |