Reland "Remove single line debug entries for non-overloaded methods"
This reverts commit e959e8451dac38f6b6ae52039a6f3b3370e05900.
Change-Id: Ib8091cdee364fad443edea15178a8303660c64b1
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
new file mode 100644
index 0000000..32d3126
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, 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.graph;
+
+public class DexDebugInfoForSingleLineMethod extends DexDebugInfo {
+
+ private static final DexDebugInfoForSingleLineMethod INSTANCE =
+ new DexDebugInfoForSingleLineMethod(0, DexString.EMPTY_ARRAY, DexDebugEvent.EMPTY_ARRAY);
+
+ private DexDebugInfoForSingleLineMethod(
+ int startLine, DexString[] parameters, DexDebugEvent[] events) {
+ super(startLine, parameters, events);
+ }
+
+ public static DexDebugInfoForSingleLineMethod getInstance() {
+ return INSTANCE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
index e17446d..b56ad04 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -21,10 +21,11 @@
* the current state using the getters after a Default event.
*/
public class DexDebugPositionState implements DexDebugEventVisitor {
+
private int currentPc = 0;
private int currentLine;
private DexString currentFile = null;
- private DexMethod currentMethod = null;
+ private DexMethod currentMethod;
private Position currentCallerPosition = null;
public DexDebugPositionState(int startLine, DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 7a70c5a..37b3626 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexDebugEventBuilder;
import com.android.tools.r8.graph.DexDebugEventVisitor;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -67,6 +68,8 @@
public class LineNumberOptimizer {
+ private static final int MAX_LINE_NUMBER = 65535;
+
// PositionRemapper is a stateful function which takes a position (represented by a
// DexDebugPositionState) and returns a remapped Position.
private interface PositionRemapper {
@@ -373,21 +376,26 @@
for (DexEncodedMethod method : methods) {
kotlinRemapper.currentMethod = method;
- List<MappedPosition> mappedPositions = new ArrayList<>();
+ List<MappedPosition> mappedPositions;
Code code = method.getCode();
if (code != null) {
if (code.isDexCode() && doesContainPositions(code.asDexCode())) {
if (appView.options().canUseDexPcAsDebugInformation() && methods.size() == 1) {
- optimizeDexCodePositionsForPc(method, appView, kotlinRemapper, mappedPositions);
+ mappedPositions = optimizeDexCodePositionsForPc(method, appView, kotlinRemapper);
} else {
- optimizeDexCodePositions(
- method, appView, kotlinRemapper, mappedPositions, identityMapping);
+ mappedPositions =
+ optimizeDexCodePositions(
+ method, appView, kotlinRemapper, identityMapping, methods.size() != 1);
}
} else if (code.isCfCode()
&& doesContainPositions(code.asCfCode())
&& !appView.isCfByteCodePassThrough(method)) {
- optimizeCfCodePositions(method, kotlinRemapper, mappedPositions, appView);
+ mappedPositions = optimizeCfCodePositions(method, kotlinRemapper, appView);
+ } else {
+ mappedPositions = new ArrayList<>();
}
+ } else {
+ mappedPositions = new ArrayList<>();
}
DexMethod originalMethod =
@@ -472,10 +480,20 @@
lastPosition = mp;
}
}
- Range obfuscatedRange =
- new Range(firstPosition.obfuscatedLine, lastPosition.obfuscatedLine);
Range originalRange = new Range(firstPosition.originalLine, lastPosition.originalLine);
+ Range obfuscatedRange;
+ if (method.getCode().isDexCode()
+ && method.getCode().asDexCode().getDebugInfo()
+ == DexDebugInfoForSingleLineMethod.getInstance()) {
+ assert firstPosition.originalLine == lastPosition.originalLine;
+ method.getCode().asDexCode().setDebugInfo(null);
+ obfuscatedRange = new Range(0, MAX_LINE_NUMBER);
+ } else {
+ obfuscatedRange =
+ new Range(firstPosition.obfuscatedLine, lastPosition.obfuscatedLine);
+ }
+
ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.get();
MappedRange lastMappedRange =
classNamingBuilder.addMappedRange(
@@ -498,6 +516,11 @@
}
i = j;
}
+ if (method.getCode().isDexCode()
+ && method.getCode().asDexCode().getDebugInfo()
+ == DexDebugInfoForSingleLineMethod.getInstance()) {
+ method.getCode().asDexCode().setDebugInfo(null);
+ }
} // for each method of the group
} // for each method group, grouped by name
} // for each class
@@ -667,14 +690,15 @@
return false;
}
- private static void optimizeDexCodePositions(
+ private static List<MappedPosition> optimizeDexCodePositions(
DexEncodedMethod method,
AppView<?> appView,
PositionRemapper positionRemapper,
- List<MappedPosition> mappedPositions,
- boolean identityMapping) {
+ boolean identityMapping,
+ boolean hasOverloads) {
+ List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
- final DexApplication application = appView.appInfo().app();
+ DexApplication application = appView.appInfo().app();
DexCode dexCode = method.getCode().asDexCode();
DexDebugInfo debugInfo = dexCode.getDebugInfo();
List<DexDebugEvent> processedEvents = new ArrayList<>();
@@ -688,7 +712,7 @@
Box<Boolean> inlinedOriginalPosition = new Box<>(false);
// Debug event visitor to map line numbers.
- DexDebugEventVisitor visitor =
+ DexDebugPositionState visitor =
new DexDebugPositionState(
debugInfo.startLine,
appView.graphLens().getOriginalMethodSignature(method.getReference())) {
@@ -768,6 +792,15 @@
event.accept(visitor);
}
+ // If we only have one line event we can always retrace back uniquely.
+ if (mappedPositions.size() <= 1
+ && !hasOverloads
+ && !appView.options().debug
+ && appView.options().lineNumberOptimization != LineNumberOptimization.OFF) {
+ dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
+ return mappedPositions;
+ }
+
DexDebugInfo optimizedDebugInfo =
new DexDebugInfo(
positionEventEmitter.getStartLine(),
@@ -779,13 +812,12 @@
|| verifyIdentityMapping(debugInfo, optimizedDebugInfo);
dexCode.setDebugInfo(optimizedDebugInfo);
+ return mappedPositions;
}
- private static void optimizeDexCodePositionsForPc(
- DexEncodedMethod method,
- AppView<?> appView,
- PositionRemapper positionRemapper,
- List<MappedPosition> mappedPositions) {
+ private static List<MappedPosition> optimizeDexCodePositionsForPc(
+ DexEncodedMethod method, AppView<?> appView, PositionRemapper positionRemapper) {
+ List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
DexCode dexCode = method.getCode().asDexCode();
DexDebugInfo debugInfo = dexCode.getDebugInfo();
@@ -834,6 +866,7 @@
}
dexCode.setDebugInfo(null);
+ return mappedPositions;
}
private static boolean verifyIdentityMapping(
@@ -846,11 +879,9 @@
return true;
}
- private static void optimizeCfCodePositions(
- DexEncodedMethod method,
- PositionRemapper positionRemapper,
- List<MappedPosition> mappedPositions,
- AppView<?> appView) {
+ private static List<MappedPosition> optimizeCfCodePositions(
+ DexEncodedMethod method, PositionRemapper positionRemapper, AppView<?> appView) {
+ List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
CfCode oldCode = method.getCode().asCfCode();
List<CfInstruction> oldInstructions = oldCode.getInstructions();
@@ -877,6 +908,7 @@
oldCode.getTryCatchRanges(),
oldCode.getLocalVariables()),
appView);
+ return mappedPositions;
}
private static Position remapAndAdd(
diff --git a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
index 785db86..18ae157 100644
--- a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
-import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -40,7 +39,7 @@
.noMinification()
.addKeepRules("-keepattributes SourceFile,LineNumberTable")
.addProgramClasses(CLASS)
- .setMode(CompilationMode.DEBUG)
+ .debug()
.debugConfig());
}
return parameters.build();
diff --git a/src/test/java/com/android/tools/r8/debug/R8DebugNonMinifiedProgramTestRunner.java b/src/test/java/com/android/tools/r8/debug/R8DebugNonMinifiedProgramTestRunner.java
index 9771a28..c206daa 100644
--- a/src/test/java/com/android/tools/r8/debug/R8DebugNonMinifiedProgramTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/R8DebugNonMinifiedProgramTestRunner.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
@@ -37,22 +36,11 @@
private static BiFunction<Backend, AndroidApiLevel, R8TestCompileResult> compiledDebug
= memoizeBiFunction(R8DebugNonMinifiedProgramTestRunner::compileDebug);
- private static BiFunction<Backend, AndroidApiLevel, R8TestCompileResult> compiledNoOptNoMinify
- = memoizeBiFunction(R8DebugNonMinifiedProgramTestRunner::compileNoOptNoMinify);
-
private static R8TestCompileResult compileDebug(Backend backend, AndroidApiLevel apiLevel)
throws Exception {
return compile(testForR8(getStaticTemp(), backend).debug(), apiLevel);
}
- private static R8TestCompileResult compileNoOptNoMinify(Backend backend, AndroidApiLevel apiLevel)
- throws Exception {
- return compile(
- testForR8(getStaticTemp(), backend)
- .addKeepRules("-dontoptimize", "-dontobfuscate", "-keepattributes LineNumberTable"),
- apiLevel);
- }
-
private static R8TestCompileResult compile(R8FullTestBuilder builder, AndroidApiLevel apiLevel)
throws Exception {
return builder
@@ -70,24 +58,11 @@
});
}
- private void assumeMappingIsNotToPCs() {
- assumeTrue(
- "Ignoring test when the line number table is removed.",
- parameters.isCfRuntime()
- || parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport()));
- }
-
@Test
public void testDebugMode() throws Throwable {
runTest(compiledDebug.apply(parameters.getBackend(), parameters.getApiLevel()));
}
- @Test
- public void testNoOptimizationAndNoMinification() throws Throwable {
- assumeMappingIsNotToPCs();
- runTest(compiledNoOptNoMinify.apply(parameters.getBackend(), parameters.getApiLevel()));
- }
-
private void runTest(R8TestCompileResult compileResult) throws Throwable {
compileResult
.run(parameters.getRuntime(), CLASS)
diff --git a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
index 73aff7f..48b9fc8 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
@@ -66,7 +66,7 @@
result.app.write(classesPath, OutputMode.DexIndexed);
int numberOfDebugInfos =
getNumberOfDebugInfos(Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
- Assert.assertEquals(1, numberOfDebugInfos);
+ Assert.assertEquals(0, numberOfDebugInfos);
}
// Two classes which has debug info that looks exactly the same, except for SetInlineFrame.
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
index 17dadf2..4898ef3 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -140,7 +140,6 @@
int expectedLineNumber = throwLocation == Location.FOO2 ? 2 : 1;
String expectedFilePos = TEST_CLASS + ".java:" + expectedLineNumber;
int idx = line.indexOf(expectedFilePos);
- assertTrue(idx >= 0);
// And the next character must be a non-digit or nothing.
int idxAfter = idx + expectedFilePos.length();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
new file mode 100644
index 0000000..dd9bcc7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2021, 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.debuginfo;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.hasLineNumberTable;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SingleLineInfoInlineRemoveTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public SingleLineInfoInlineRemoveTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForJvm()
+ .addTestClasspath()
+ .run(CfRuntime.getSystemRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .map(StackTrace::extractFromJvm);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresent());
+ assertThat(mainSubject.uniqueMethodWithName("inlinee"), not(isPresent()));
+ assertThat(
+ mainSubject.uniqueMethodWithName("shouldRemoveLineNumberForInline"),
+ notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ });
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void printOrThrow(String message) {
+ if (System.currentTimeMillis() > 0) {
+ throw new NullPointerException(message);
+ }
+ System.out.println(message);
+ }
+
+ public static void inlinee() {
+ printOrThrow("Hello from inlinee");
+ }
+
+ @NeverInline
+ public static void shouldRemoveLineNumberForInline() {
+ inlinee();
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ shouldRemoveLineNumberForInline();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java
new file mode 100644
index 0000000..d7a5df3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2021, 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.debuginfo;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.hasLineNumberTable;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SingleLineInfoMultipleCallsRemoveTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public SingleLineInfoMultipleCallsRemoveTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForJvm()
+ .addTestClasspath()
+ .run(CfRuntime.getSystemRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .map(StackTrace::extractFromJvm);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ assertThat(inspector.clazz(Builder.class), isPresent());
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresent());
+ assertThat(
+ mainSubject.uniqueMethodWithName("shouldRemoveLineNumberForMultipleInvokes"),
+ notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ assertThat(
+ mainSubject.uniqueMethodWithName("main"),
+ notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ });
+ }
+
+ @NeverClassInline
+ public static class Builder {
+
+ StringBuilder sb = new StringBuilder();
+
+ @NeverInline
+ public Builder add(String str) {
+ sb.append(str);
+ return this;
+ }
+
+ @NeverInline
+ public String build() {
+ return sb.toString();
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void printOrThrow(String message) {
+ if (System.currentTimeMillis() > 0) {
+ throw new NullPointerException(message);
+ }
+ System.out.println(message);
+ }
+
+ @NeverInline
+ public static void shouldRemoveLineNumberForMultipleInvokes() {
+ printOrThrow(new Builder().add("foo").add("bar").add("baz").build());
+ }
+
+ public static void main(String[] args) {
+ shouldRemoveLineNumberForMultipleInvokes();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
new file mode 100644
index 0000000..bc0d012
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleInlineTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2021, 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.debuginfo;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.hasLineNumberTable;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SingleLineInfoMultipleInlineTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public SingleLineInfoMultipleInlineTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForJvm()
+ .addTestClasspath()
+ .run(CfRuntime.getSystemRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .map(StackTrace::extractFromJvm);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresent());
+ assertThat(mainSubject.uniqueMethodWithName("inlinee"), not(isPresent()));
+ assertThat(
+ mainSubject.uniqueMethodWithName("shouldNotRemoveLineNumberForInline"),
+ notIf(
+ hasLineNumberTable(),
+ parameters.isDexRuntime()
+ && parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())));
+ });
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void printOrThrow(String message) {
+ if (System.currentTimeMillis() > 0) {
+ throw new NullPointerException(message);
+ }
+ System.out.println(message);
+ }
+
+ public static void inlinee() {
+ printOrThrow("Hello from inlinee");
+ }
+
+ public static void inlinee2() {
+ printOrThrow("Hello from inlinee2");
+ }
+
+ @NeverInline
+ public static void shouldNotRemoveLineNumberForInline() {
+ inlinee();
+ inlinee2();
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ shouldNotRemoveLineNumberForInline();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java
new file mode 100644
index 0000000..652f401
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, 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.debuginfo;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.hasLineNumberTable;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SingleLineInfoRemoveTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public SingleLineInfoRemoveTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForJvm()
+ .addTestClasspath()
+ .run(CfRuntime.getSystemRuntime(), Main.class)
+ .assertFailure()
+ .map(StackTrace::extractFromJvm);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresent());
+ assertThat(
+ mainSubject.uniqueMethodWithName("shouldRemoveLineNumber"),
+ notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ });
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static void printOrThrow(String message) {
+ if (System.currentTimeMillis() > 0) {
+ throw new NullPointerException(message);
+ }
+ System.out.println(message);
+ }
+
+ @NeverInline
+ public static void shouldRemoveLineNumber() {
+ printOrThrow("Hello World");
+ }
+
+ public static void main(String[] args) {
+ shouldRemoveLineNumber();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
deleted file mode 100644
index 56f306c..0000000
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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.naming.retrace;
-
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileName;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestBuilder;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.google.common.collect.ImmutableList;
-import java.util.Collection;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest extends RetraceTestBase {
-
- @Parameters(name = "{0}, mode: {1}, compat: {2}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(),
- CompilationMode.values(),
- BooleanUtils.values());
- }
-
- public DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest(
- TestParameters parameters, CompilationMode mode, boolean compat) {
- super(parameters, mode, compat);
- }
-
- @Override
- public void configure(R8TestBuilder<?> builder) {
- builder.enableInliningAnnotations();
- }
-
- @Override
- public Collection<Class<?>> getClasses() {
- return ImmutableList.of(
- getMainClass(), InterfaceWithStaticMethod1.class, InterfaceWithStaticMethod2.class);
- }
-
- @Override
- public Class<?> getMainClass() {
- return MainDesugarStaticInterfaceMethodsRetraceTest.class;
- }
-
- @Test
- public void testSourceFileAndLineNumberTable() throws Exception {
- runTest(
- ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
- // Companion methods are treated as having inlined the interface method code.
- // If compiling with synthetic marking support in the mapping file, the synthetic frames
- // are removed and the trace will be equal to RI.
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) ->
- assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace)));
- }
-}
-
-interface InterfaceWithStaticMethod2 {
-
- static void staticMethod2() {
- throw null;
- }
-}
-
-interface InterfaceWithStaticMethod1 {
-
- @NeverInline
- static void staticMethod1() {
- InterfaceWithStaticMethod2.staticMethod2();
- }
-}
-
-class MainDesugarStaticInterfaceMethodsRetraceTest {
-
- public static void main(String[] args) {
- InterfaceWithStaticMethod1.staticMethod1();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 7a78ed0..6e83620 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -567,6 +567,10 @@
};
}
+ public static Matcher<MethodSubject> hasLineNumberTable() {
+ return isMethodSatisfying("line number table", method -> method.getLineNumberTable() != null);
+ }
+
public static Matcher<RetraceFrameResult> isInlineFrame() {
return new TypeSafeMatcher<RetraceFrameResult>() {
@Override