From 5f3cd8683d8b2e7429e73c2fa7199232ea7c46ca Mon Sep 17 00:00:00 2001 From: James Iry Date: Mon, 25 Feb 2013 16:30:46 -0800 Subject: SI-7181 Eliminate unnecessary duplication of finally blocks The main body of a try and each exception handler were getting a copy of the finally block for the "normal" flow case (i.e. where they don't throw an uncaught exception or use "return" to exit early). But that's not necessary. With this commit the try body and each exception handler can all jump to the same copy of the finally block on a normal exit. A byte code test is included to ensure we're getting fewer copies of the finally block. inline-ex-handlers.check is updated because the icode is a bit different without the extra finally block copies. --- test/files/jvm/t7181/Foo_1.scala | 26 ++++++++++++++++++++++++++ test/files/jvm/t7181/Test.scala | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/files/jvm/t7181/Foo_1.scala create mode 100644 test/files/jvm/t7181/Test.scala (limited to 'test/files/jvm/t7181') diff --git a/test/files/jvm/t7181/Foo_1.scala b/test/files/jvm/t7181/Foo_1.scala new file mode 100644 index 0000000000..f9dfdd4442 --- /dev/null +++ b/test/files/jvm/t7181/Foo_1.scala @@ -0,0 +1,26 @@ +class Exception1 extends RuntimeException +class Exception2 extends RuntimeException + +class Foo_1 { + def foo(baz: Baz) { + try { + baz.bar + } catch { + case _: Exception1 => println("exception 1") + case _: Exception2 => println("exception 2") + } finally { + // this should be the only copy of the magic constant 3 + // making it easy to detect copies of this finally block + println(s"finally ${3}") + } + println(s"normal flow") + } +} + +trait Baz { + // does it throw? who knows? This way + // I can ensure that no optimization that honors + // separate compilation could ever + // change the exception handling structure + def bar: Unit +} diff --git a/test/files/jvm/t7181/Test.scala b/test/files/jvm/t7181/Test.scala new file mode 100644 index 0000000000..35dba436c1 --- /dev/null +++ b/test/files/jvm/t7181/Test.scala @@ -0,0 +1,24 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import asm.tree.InsnList +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("Foo_1") + val methodNode = getMethod(classNode, "foo") + // there should be 2 copies of the finally block, each with the magic constant 3 + // one for the "normal" exit + // one for the uncaught exception exit + // prior to this PR there would have been 4 since each exception handler would also get a copy + val expected = 2 + val got = countMagicThrees(methodNode.instructions) + assert(got == expected, s"expected $expected but got $got magic threes") + } + + def countMagicThrees(insnList: InsnList): Int = { + def isMagicThree(node: asm.tree.AbstractInsnNode): Boolean = + (node.getOpcode == asm.Opcodes.ICONST_3) + insnList.iterator.asScala.count(isMagicThree) + } +} -- cgit v1.2.3