Invalid StackMapTable for exception handlers in CF
When re-using same object for calls in try-catch-finally blocks, we
might duplicate the object on the stack before storing in local. If
the exception table block spans over the dup instruction we will get
an error.
See b/122445224 for further info.
Bug: 122445224
Change-Id: If2dbdfdfd8b3f6dff9a63958d291e9f8713dae57
diff --git a/src/test/java/com/android/tools/r8/cf/CloserTest.java b/src/test/java/com/android/tools/r8/cf/CloserTest.java
new file mode 100644
index 0000000..874eb8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CloserTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.NeverInline;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class CloserTest {
+
+ @NeverInline
+ public static CloserTest create() {
+ return new CloserTest();
+ }
+
+ OutputStream register() {
+ return System.out;
+ }
+
+ public void doSomething(String message) throws IOException {
+ System.out.println(message);
+ }
+
+ public static void main(String... args) throws IOException {
+ CloserTest closer = CloserTest.create();
+ try {
+ OutputStream out = closer.register();
+ out.write(args[0].getBytes());
+ } catch (Throwable e) {
+ closer.doSomething(e.getMessage());
+ } finally {
+ closer.doSomething("FINISHED");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java b/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java
new file mode 100644
index 0000000..a59ee83
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import org.junit.Test;
+
+/**
+ * This tests that we produce valid code when having normal-flow with exceptional edges in blocks.
+ * We might perform optimizations that add stack-operations (dup, swap, etc.) before and after
+ * instructions that lie on the boundary of the exception table that is generated for a basic block:
+ *
+ * <pre>
+ * Code:
+ * 0: invokestatic #16 // Method create:()Lcom/android/tools/r8/cf/CloserTest;
+ * 3: dup
+ * 4: dup
+ * 5: astore_1
+ * ...
+ * Exception table:
+ * from to target type
+ * 3 9 24 Class java/lang/Throwable
+ * ...
+ * StackMap table:
+ * StackMapTable: number_of_entries = 4
+ * frame_type = 255
+ * offset_delta=21
+ * locals=[top, class com/android/tools/r8/cf/CloserTest]
+ * stack=[class java/lang/Throwable]
+ * </pre>
+ *
+ * If we produce something like this, the JVM verifier will throw a VerifyError on @bci: 3 since we
+ * have not stored CloserTest in locals[1] (that happens in @bci 5), as described in the
+ * StackMapTable. This is because the exception handler starts at @bci 3 and not later.
+ * TODO(b/122445224)
+ */
+public class CloserTestRunner extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ testForR8(Backend.CF)
+ .addProgramClasses(CloserTest.class)
+ .addKeepMainRule(CloserTest.class)
+ .setMode(CompilationMode.RELEASE)
+ .minification(false)
+ .noTreeShaking()
+ .enableInliningAnnotations()
+ .compile()
+ .run(CloserTest.class)
+ .assertFailure();
+ }
+}