[Retrace] Add test for retracing inlined pruned method
Bug: b/226885646
Change-Id: I8071d70e8ab4faaa5faa2ea5c4f15114a2af310b
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index 26c33f4..6891000 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -10,17 +10,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.objectweb.asm.Label;
@RunWith(Parameterized.class)
public class NoLineInfoTest extends TestBase {
@@ -47,21 +42,7 @@
private byte[] getTestClassTransformed() throws IOException {
return transformer(TestClass.class)
.setSourceFile(INPUT_SOURCE_FILE)
- .addMethodTransformer(
- new MethodTransformer() {
- private final Map<MethodReference, Integer> lines = new HashMap<>();
-
- @Override
- public void visitLineNumber(int line, Label start) {
- Integer nextLine = lines.getOrDefault(getContext().getReference(), 0);
- if (nextLine > 0) {
- super.visitLineNumber(nextLine, start);
- }
- // Increment the actual line content by 100 so that each one is clearly distinct
- // from a PC value for any of the methods.
- lines.put(getContext().getReference(), nextLine + 100);
- }
- })
+ .setPredictiveLineNumbering()
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index f4158a1..4fdc4b7 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -71,6 +71,24 @@
return this;
}
+ public Builder addWithoutLineNumber(Class<?> clazz, String methodName, String fileName) {
+ return addWithoutLineNumber(clazz.getTypeName(), methodName, fileName);
+ }
+
+ public Builder addWithoutLineNumber(ClassReference clazz, String methodName, String fileName) {
+ return addWithoutLineNumber(clazz.getTypeName(), methodName, fileName);
+ }
+
+ public Builder addWithoutLineNumber(String className, String methodName, String fileName) {
+ stackTraceLines.add(
+ StackTraceLine.builder()
+ .setClassName(className)
+ .setMethodName(methodName)
+ .setFileName(fileName)
+ .build());
+ return this;
+ }
+
public Builder map(int i, Function<StackTraceLine, StackTraceLine> map) {
stackTraceLines.set(i, map.apply(stackTraceLines.get(i)));
return this;
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineFunctionInPrunedClassTest.java b/src/test/java/com/android/tools/r8/retrace/InlineFunctionInPrunedClassTest.java
new file mode 100644
index 0000000..d8e9673
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/InlineFunctionInPrunedClassTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, 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.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.Sets;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InlineFunctionInPrunedClassTest extends TestBase {
+
+ private static final String NEW_SOURCE_FILE = "SourceFileA.java";
+ private static final String ORIGINAL_SOURCE_FILE = "InlineFunctionInPrunedClassTest.java";
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getAWithCustomSourceFile(), getMainWithStaticPosition())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(stackTrace -> checkExpectedStackTrace(stackTrace, false));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getAWithCustomSourceFile(), getMainWithStaticPosition())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ // Ensure we have inlined the A class and it is not in the output.
+ assertEquals(
+ Sets.newHashSet(typeName(Main.class)),
+ inspector.allClasses().stream()
+ .map(FoundClassSubject::getFinalName)
+ .collect(Collectors.toSet()));
+ checkExpectedStackTrace(stackTrace, true);
+ });
+ }
+
+ private void checkExpectedStackTrace(StackTrace stackTrace, boolean errorInASourceFile) {
+ assertThat(
+ stackTrace,
+ isSame(
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(A.class))
+ .setMethodName("foo")
+ // TODO(b/226885646): We should keep the original source file.
+ .setFileName(errorInASourceFile ? ORIGINAL_SOURCE_FILE : NEW_SOURCE_FILE)
+ .setLineNumber(1)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(Main.class))
+ .setMethodName("main")
+ .setFileName(ORIGINAL_SOURCE_FILE)
+ .setLineNumber(1)
+ .build())
+ .build()));
+ }
+
+ private static byte[] getAWithCustomSourceFile() throws Exception {
+ return transformer(A.class)
+ .setSourceFile(NEW_SOURCE_FILE)
+ .setPredictiveLineNumbering(MethodPredicate.all(), 1)
+ .transform();
+ }
+
+ private static byte[] getMainWithStaticPosition() throws Exception {
+ return transformer(Main.class).setPredictiveLineNumbering(MethodPredicate.all(), 1).transform();
+ }
+
+ public static class A {
+
+ public static void foo() {
+ throw new NullPointerException();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 52485b2..cbdbde0 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -32,7 +32,9 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -1064,6 +1066,38 @@
});
}
+ public ClassFileTransformer setPredictiveLineNumbering() {
+ return setPredictiveLineNumbering(MethodPredicate.all());
+ }
+
+ public ClassFileTransformer setPredictiveLineNumbering(MethodPredicate predicate) {
+ return setPredictiveLineNumbering(predicate, 0);
+ }
+
+ public ClassFileTransformer setPredictiveLineNumbering(
+ MethodPredicate predicate, int startingLineNumber) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ private final Map<MethodReference, Integer> lines = new HashMap<>();
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ if (MethodPredicate.testContext(predicate, getContext())) {
+ Integer nextLine =
+ lines.getOrDefault(getContext().getReference(), startingLineNumber);
+ if (nextLine > 0) {
+ super.visitLineNumber(nextLine, start);
+ }
+ // Increment the actual line content by 100 so that each one is clearly distinct
+ // from a PC value for any of the methods.
+ lines.put(getContext().getReference(), nextLine + 100);
+ } else {
+ super.visitLineNumber(line, start);
+ }
+ }
+ });
+ }
+
@FunctionalInterface
private interface VisitMethodInsnCallback {
void visitMethodInsn(