[Retrace] Add support for circular reference exception lines
Bug: 147975151
Change-Id: I9c6c5f9021e8267c9e77195a52ef158557aa652f
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index 3b68d3d..8d3e956 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -458,6 +458,69 @@
}
}
+ static class CircularReferenceLine extends StackTraceLine {
+
+ private final String startWhitespace;
+ private final String exceptionClass;
+ private final String endBracketAndWhitespace;
+
+ private static final String CIRCULAR_REFERENCE = "[CIRCULAR REFERENCE:";
+
+ public CircularReferenceLine(
+ String startWhitespace, String exceptionClass, String endBracketAndWhitespace) {
+ this.startWhitespace = startWhitespace;
+ this.exceptionClass = exceptionClass;
+ this.endBracketAndWhitespace = endBracketAndWhitespace;
+ }
+
+ static StackTraceLine tryParse(String line) {
+ // Check that the line is indented with some amount of white space.
+ if (line.length() == 0 || !Character.isWhitespace(line.charAt(0))) {
+ return null;
+ }
+ // Find the first non-white space character and check that we have the sequence
+ // '[CIRCULAR REFERENCE:Exception]'.
+ int firstNonWhiteSpace = firstNonWhiteSpaceCharacterFromIndex(line, 0);
+ if (!line.startsWith(CIRCULAR_REFERENCE, firstNonWhiteSpace)) {
+ return null;
+ }
+ int exceptionStartIndex = firstNonWhiteSpace + CIRCULAR_REFERENCE.length();
+ int lastBracketPosition = firstCharFromIndex(line, exceptionStartIndex, ']');
+ if (lastBracketPosition == line.length()) {
+ return null;
+ }
+ int onlyWhitespaceFromLastBracket =
+ firstNonWhiteSpaceCharacterFromIndex(line, lastBracketPosition + 1);
+ if (onlyWhitespaceFromLastBracket != line.length()) {
+ return null;
+ }
+ return new CircularReferenceLine(
+ line.substring(0, firstNonWhiteSpace),
+ line.substring(exceptionStartIndex, lastBracketPosition),
+ line.substring(lastBracketPosition));
+ }
+
+ @Override
+ List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> exceptionLines = new ArrayList<>();
+ retraceBase
+ .retrace(Reference.classFromTypeName(exceptionClass))
+ .forEach(
+ element ->
+ exceptionLines.add(
+ new CircularReferenceLine(
+ startWhitespace,
+ element.getClassReference().getTypeName(),
+ endBracketAndWhitespace)));
+ return exceptionLines;
+ }
+
+ @Override
+ public String toString() {
+ return startWhitespace + CIRCULAR_REFERENCE + exceptionClass + endBracketAndWhitespace;
+ }
+ }
+
static class UnknownLine extends StackTraceLine {
private final String line;
@@ -490,6 +553,10 @@
if (parsedLine != null) {
return parsedLine;
}
+ parsedLine = CircularReferenceLine.tryParse(line);
+ if (parsedLine != null) {
+ return parsedLine;
+ }
parsedLine = MoreLine.tryParse(line);
if (parsedLine == null) {
diagnosticsHandler.warning(
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index e29c928..e50ca91 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -8,6 +8,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -18,6 +19,7 @@
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousMissingLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousStackTrace;
+import com.android.tools.r8.retrace.stacktraces.CircularReferenceStackTrace;
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
@@ -149,6 +151,14 @@
runRetraceTest(new InlineNoLineNumberStackTrace());
}
+ @Test
+ public void testCircularReferenceStackTrace() {
+ // Proguard retrace (and therefore the default regular expression) will not retrace circular
+ // reference exceptions.
+ assumeFalse(useRegExpParsing);
+ runRetraceTest(new CircularReferenceStackTrace());
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/CircularReferenceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/CircularReferenceStackTrace.java
new file mode 100644
index 0000000..5df37f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/CircularReferenceStackTrace.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2020, 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.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class CircularReferenceStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ " [CIRCULAR REFERENCE:A.A]",
+ " [CIRCULAR REFERENCE:A.B]",
+ " [CIRCULAR REFERENCE:None.existing.class]",
+ " [CIRCULAR REFERENCE:A.A] ",
+ // Invalid Circular Reference lines.
+ " [CIRCU:AA]",
+ " [CIRCULAR REFERENCE:A.A",
+ " [CIRCULAR REFERENCE:]",
+ " [CIRCULAR REFERENCE:None existing class]");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines("foo.bar.Baz -> A.A:", "foo.bar.Qux -> A.B:");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ " [CIRCULAR REFERENCE:foo.bar.Baz]",
+ " [CIRCULAR REFERENCE:foo.bar.Qux]",
+ " [CIRCULAR REFERENCE:None.existing.class]",
+ " [CIRCULAR REFERENCE:foo.bar.Baz] ",
+ " [CIRCU:AA]",
+ " [CIRCULAR REFERENCE:A.A",
+ " [CIRCULAR REFERENCE:]",
+ " [CIRCULAR REFERENCE:None existing class]");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 5;
+ }
+}