Merge "Test for stepping into a method with local changes prior to the first line."
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index efec188..5c6abba 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -736,7 +736,7 @@
}
}
- private String extractClassName(byte[] ccc) {
+ public String extractClassName(byte[] ccc) {
class ClassNameExtractor extends ClassVisitor {
private String className;
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index b6cdb48..445dd53 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@@ -508,6 +509,15 @@
return t -> inspector.accept(t.debuggeeState);
}
+ protected final JUnit3Wrapper.Command conditional(
+ Function<JUnit3Wrapper.DebuggeeState, List<JUnit3Wrapper.Command>> conditional) {
+ return t -> subcommands(conditional.apply(t.debuggeeState)).perform(t);
+ }
+
+ protected final JUnit3Wrapper.Command subcommands(List<JUnit3Wrapper.Command> commands) {
+ return t -> Lists.reverse(commands).forEach(t.commandsQueue::addFirst);
+ }
+
protected final JUnit3Wrapper.Command setLocal(String localName, Value newValue) {
return new JUnit3Wrapper.Command.SetLocalCommand(localName, newValue);
}
diff --git a/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest.java b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest.java
new file mode 100644
index 0000000..027d709
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2018, 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.debug;
+
+import java.util.Collection;
+import java.util.List;
+
+public class StepIntoMethodWithTypeParameterArgumentsTest {
+
+ public static List<Object> field = null;
+
+ public static void foo(List<String> strings) {
+ Collection<Object> objects = field;
+ }
+
+ public static void main(String[] args) {
+ foo(null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestDump.java b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestDump.java
new file mode 100644
index 0000000..ef1cab1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestDump.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2018, 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.debug;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class StepIntoMethodWithTypeParameterArgumentsTestDump implements Opcodes {
+
+ public static byte[] dump(boolean moveFirstLineEntry) {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ FieldVisitor fieldVisitor;
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ "com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest",
+ null,
+ "java/lang/Object",
+ null);
+
+ classWriter.visitSource("StepIntoMethodWithTypeParameterArgumentsTest.java", null);
+
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_PUBLIC | ACC_STATIC,
+ "field",
+ "Ljava/util/List;",
+ "Ljava/util/List<Ljava/lang/Object;>;",
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(9, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable(
+ "this",
+ "Lcom/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest;",
+ null,
+ label0,
+ label1,
+ 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC,
+ "foo",
+ "(Ljava/util/List;)V",
+ "(Ljava/util/List<Ljava/lang/String;>;)V",
+ null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ if (!moveFirstLineEntry) {
+ // Removed to have 'strings' start at undefined line number.
+ methodVisitor.visitLineNumber(14, label0);
+ }
+ methodVisitor.visitFieldInsn(
+ GETSTATIC,
+ "com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest",
+ "field",
+ "Ljava/util/List;");
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ if (moveFirstLineEntry) {
+ // Move the line entry to after 'strings' has already become live.
+ Label labelNop = new Label();
+ methodVisitor.visitLabel(labelNop);
+ methodVisitor.visitLineNumber(14, labelNop);
+ methodVisitor.visitInsn(NOP);
+ }
+ // Now at line 13 objects will be live.
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(15, label1);
+ methodVisitor.visitInsn(RETURN);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLocalVariable(
+ "strings", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", label0, label2, 0);
+ methodVisitor.visitLocalVariable(
+ "objects",
+ "Ljava/util/Collection;",
+ "Ljava/util/Collection<Ljava/lang/Object;>;",
+ label1,
+ label2,
+ 1);
+ methodVisitor.visitMaxs(1, 2);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(18, label0);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest",
+ "foo",
+ "(Ljava/util/List;)V",
+ false);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(19, label1);
+ methodVisitor.visitInsn(RETURN);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(11, label0);
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitFieldInsn(
+ PUTSTATIC,
+ "com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTest",
+ "field",
+ "Ljava/util/List;");
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(1, 0);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java
new file mode 100644
index 0000000..aa4542a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2018, 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.debug;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.junit.Test;
+
+/** Tests debugging behavior with regards to exception handling */
+public class StepIntoMethodWithTypeParameterArgumentsTestRunner extends DebugTestBase {
+
+ private static final Class CLASS = StepIntoMethodWithTypeParameterArgumentsTest.class;
+ private static final String NAME = CLASS.getCanonicalName();
+ private static final String DESC = DescriptorUtils.javaTypeToDescriptor(NAME);
+ private static final String FILE = CLASS.getSimpleName() + ".java";
+
+ @Test
+ public void testCf() throws Throwable {
+ byte[] bytes = StepIntoMethodWithTypeParameterArgumentsTestDump.dump(true);
+ assertEquals(NAME, extractClassName(bytes));
+ // Java jumps to first instruction of the catch handler, matching the source code.
+ Path jar = temp.getRoot().toPath().resolve("test.jar");
+ ArchiveConsumer archiveConsumer = new ArchiveConsumer(jar);
+ archiveConsumer.accept(ByteDataView.of(bytes), DESC, null);
+ archiveConsumer.finished(null);
+ run(new CfDebugTestConfig().addPaths(jar));
+ }
+
+ @Test
+ public void testD8() throws Throwable {
+ Path out = temp.getRoot().toPath().resolve("out.jar");
+ D8.run(
+ D8Command.builder()
+ .addClassProgramData(
+ StepIntoMethodWithTypeParameterArgumentsTestDump.dump(true), Origin.unknown())
+ .setOutput(out, OutputMode.DexIndexed)
+ .build());
+ run(new DexDebugTestConfig().addPaths(out));
+ }
+
+ private void run(DebugTestConfig config) throws Throwable {
+ runDebugTest(
+ config,
+ NAME,
+ breakpoint(NAME, "main"),
+ run(),
+ checkLine(FILE, 18), // First line in main.
+ stepInto(),
+ checkLine(FILE, -1), // First line in foo is undefined due to ASM dump change.
+ checkLocal("strings"),
+ checkNoLocal("objects"),
+ stepOver(),
+ // Step will skip line 14 and hit 15 on JVM but will (correctly?) hit 14 on Art.
+ subcommands(
+ config instanceof CfDebugTestConfig
+ ? ImmutableList.of()
+ : ImmutableList.of(checkLine(FILE, 14), stepOver())),
+ checkLine(FILE, 15),
+ checkLocal("strings"),
+ checkLocal("objects"),
+ run());
+ }
+}