[Retrace] Filter map lines before materializing of strings
Bug: b/226892337
Bug: b/226885646
Change-Id: I616f76ddb24fa75d757f90a62e6ff576934cf77c
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 7657adc..0b0d20f 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -47,16 +47,6 @@
private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>();
private LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>();
- private final Set<String> buildForClass;
-
- private Builder(Set<String> buildForClass) {
- this.buildForClass = buildForClass;
- }
-
- @Override
- public boolean buildForClass(String typeName) {
- return buildForClass == null || buildForClass.contains(typeName);
- }
@Override
public ClassNamingForNameMapper.Builder classNamingBuilder(
@@ -88,11 +78,7 @@
}
public static Builder builder() {
- return new Builder(null);
- }
-
- public static Builder builder(Set<String> buildForClass) {
- return new Builder(buildForClass);
+ return new Builder();
}
public static ClassNameMapper mapperFromFile(Path path) throws IOException {
@@ -182,7 +168,7 @@
diagnosticsHandler != null ? diagnosticsHandler : new Reporter(),
allowEmptyMappedRanges,
allowExperimentalMapping)) {
- ClassNameMapper.Builder builder = ClassNameMapper.builder(buildForClass);
+ ClassNameMapper.Builder builder = ClassNameMapper.builder();
proguardReader.parse(builder);
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
index d1e7463..6b95444 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMap.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -16,10 +16,6 @@
abstract Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion);
abstract ProguardMap build();
-
- public boolean buildForClass(String typeName) {
- return true;
- }
}
boolean hasMapping(DexType type);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index bbea4c4..f28d4bc 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -261,18 +261,11 @@
String after = parseType(false);
skipWhitespace();
expect(':');
- if (mapBuilder.buildForClass(after)) {
- ClassNaming.Builder currentClassBuilder =
- mapBuilder.classNamingBuilder(after, before, getPosition());
- skipWhitespace();
- if (nextLine()) {
- parseMemberMappings(currentClassBuilder);
- }
- } else {
- do {
- lineOffset = line.length();
- nextLine();
- } while (hasLine() && !isClassMapping());
+ ClassNaming.Builder currentClassBuilder =
+ mapBuilder.classNamingBuilder(after, before, getPosition());
+ skipWhitespace();
+ if (nextLine()) {
+ parseMemberMappings(currentClassBuilder);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
index 279a038..d42da83 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.retrace.internal;
+import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.COMPLETE_CLASS_MAPPING;
+import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.IS_COMMENT_SOURCE_FILE;
import static java.lang.Integer.MAX_VALUE;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.LineReader;
import java.io.IOException;
import java.io.InputStream;
@@ -15,12 +18,182 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import java.util.Set;
public abstract class ProguardMapReaderWithFiltering implements LineReader {
+ // The LineParserState encodes a simple state that the line parser can be in, where the
+ // (successful) transitions allowed are:
+
+ // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START
+
+ // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE
+
+ // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING
+ //
+ // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE.
+ // The terminal states are:
+ // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE }
+ //
+ public enum LineParserState {
+ BEGINNING,
+ BEGINNING_NO_WHITESPACE,
+ SEEN_ORIGINAL_CLASS,
+ SEEN_ARROW,
+ SEEN_OBFUSCATED_CLASS,
+ COMPLETE_CLASS_MAPPING,
+ IS_COMMENT_START,
+ IS_COMMENT_SOURCE_FILE,
+ NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+
+ private boolean isTerminal() {
+ return this == NOT_CLASS_MAPPING_OR_SOURCE_FILE
+ || this == COMPLETE_CLASS_MAPPING
+ || this == IS_COMMENT_SOURCE_FILE;
+ }
+
+ private static int currentIndex;
+ private static int endIndex;
+ private static byte[] bytes;
+ private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes();
+
+ public static LineParserState computeState(byte[] bytes, int startIndex, int endIndex) {
+ currentIndex = startIndex;
+ LineParserState.endIndex = endIndex;
+ LineParserState.bytes = bytes;
+ LineParserState currentState = BEGINNING;
+ while (!currentState.isTerminal()) {
+ currentState = currentState.computeNextState();
+ }
+ return currentState;
+ }
+
+ private LineParserState computeNextState() {
+ assert this != NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ switch (this) {
+ case BEGINNING:
+ return readUntilNoWhiteSpace()
+ ? BEGINNING_NO_WHITESPACE
+ : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ case BEGINNING_NO_WHITESPACE:
+ if (isCommentChar()) {
+ return IS_COMMENT_START;
+ } else {
+ int readLength = readCharactersNoWhiteSpaceUntil(' ');
+ return readLength > 0 ? SEEN_ORIGINAL_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ }
+ case SEEN_ORIGINAL_CLASS:
+ return readArrow() ? SEEN_ARROW : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ case SEEN_ARROW:
+ int colonIndex = readCharactersNoWhiteSpaceUntil(':');
+ return colonIndex > 0 ? SEEN_OBFUSCATED_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ case SEEN_OBFUSCATED_CLASS:
+ boolean read = readColon();
+ if (!read) {
+ return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ }
+ boolean noWhiteSpace = readUntilNoWhiteSpace();
+ return (!noWhiteSpace || isCommentChar())
+ ? COMPLETE_CLASS_MAPPING
+ : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ case IS_COMMENT_START:
+ if (readCharactersUntil('{')
+ && readCharactersUntil(':')
+ && readSingleOrDoubleQuote()
+ && readSourceFile()) {
+ return IS_COMMENT_SOURCE_FILE;
+ } else {
+ return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ }
+ default:
+ assert isTerminal();
+ throw new Unreachable("Should not compute next state on terminal state");
+ }
+ }
+
+ private boolean readColon() {
+ return read(':');
+ }
+
+ private boolean readCharactersUntil(char ch) {
+ while (currentIndex < endIndex) {
+ if (bytes[currentIndex++] == ch) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int readCharactersNoWhiteSpaceUntil(char ch) {
+ int startIndex = currentIndex;
+ while (currentIndex < endIndex) {
+ byte readByte = bytes[currentIndex];
+ if (readByte == ch) {
+ return currentIndex - startIndex;
+ }
+ if (Character.isWhitespace(readByte)) {
+ return -1;
+ }
+ currentIndex++;
+ }
+ return -1;
+ }
+
+ private boolean readUntilNoWhiteSpace() {
+ while (currentIndex < endIndex) {
+ if (!Character.isWhitespace(bytes[currentIndex])) {
+ return true;
+ }
+ currentIndex++;
+ }
+ return false;
+ }
+
+ private boolean readArrow() {
+ return readSpace() && read('-') && read('>') && readSpace();
+ }
+
+ private boolean readSpace() {
+ return read(' ');
+ }
+
+ private boolean read(char ch) {
+ return bytes[currentIndex++] == ch;
+ }
+
+ private boolean isCommentChar() {
+ return bytes[currentIndex] == '#';
+ }
+
+ private boolean readSourceFile() {
+ if (endIndex - currentIndex < SOURCE_FILE_BYTES.length) {
+ return false;
+ }
+ int endSourceFileIndex = currentIndex + SOURCE_FILE_BYTES.length;
+ int sourceFileByteIndex = 0;
+ for (; currentIndex < endSourceFileIndex; currentIndex++) {
+ if (SOURCE_FILE_BYTES[sourceFileByteIndex++] != bytes[currentIndex]) {
+ return false;
+ }
+ }
+ return readSingleOrDoubleQuote();
+ }
+
+ private boolean readSingleOrDoubleQuote() {
+ byte readByte = bytes[currentIndex++];
+ return readByte == '\'' || readByte == '"';
+ }
+ }
+
private int startIndex = 0;
private int endIndex = 0;
+ private final Set<String> filter;
+
+ protected ProguardMapReaderWithFiltering(Set<String> filter) {
+ this.filter = filter;
+ }
+
public abstract byte[] read() throws IOException;
public abstract int getStartIndex();
@@ -29,15 +202,43 @@
public abstract boolean exceedsBuffer();
+ private boolean isInsideClassOfInterest = false;
+ private boolean seenFirstClass = false;
+
@Override
public String readLine() throws IOException {
- byte[] bytes = readLineFromMultipleReads();
- if (bytes == null) {
- return null;
+ while (true) {
+ byte[] bytes = readLineFromMultipleReads();
+ if (bytes == null) {
+ return null;
+ }
+ if (filter == null) {
+ return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
+ }
+ LineParserState lineParserState = LineParserState.computeState(bytes, startIndex, endIndex);
+ if (lineParserState == COMPLETE_CLASS_MAPPING) {
+ seenFirstClass = true;
+ String classMapping = getBufferAsString(bytes);
+ String obfuscatedClassName = getObfuscatedClassName(classMapping);
+ isInsideClassOfInterest = filter.contains(obfuscatedClassName);
+ return classMapping;
+ } else if (lineParserState == IS_COMMENT_SOURCE_FILE) {
+ return getBufferAsString(bytes);
+ } else if (isInsideClassOfInterest || !seenFirstClass) {
+ return getBufferAsString(bytes);
+ }
}
+ }
+
+ private String getBufferAsString(byte[] bytes) {
return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
}
+ private String getObfuscatedClassName(String classMapping) {
+ int arrowIndex = classMapping.indexOf(">");
+ return classMapping.substring(arrowIndex + 2, classMapping.length() - 1);
+ }
+
private byte[] readLineFromMultipleReads() throws IOException {
startIndex = 0;
endIndex = 0;
@@ -82,7 +283,9 @@
private int currentPosition = 0;
private int temporaryBufferPosition = 0;
- public ProguardMapReaderWithFilteringMappedBuffer(Path mappingFile) throws IOException {
+ public ProguardMapReaderWithFilteringMappedBuffer(
+ Path mappingFile, Set<String> classNamesOfInterest) throws IOException {
+ super(classNamesOfInterest);
fileChannel = FileChannel.open(mappingFile, StandardOpenOption.READ);
channelSize = fileChannel.size();
readFromChannel();
@@ -160,7 +363,9 @@
private int endIndex = 0;
private int endReadIndex = 0;
- public ProguardMapReaderWithFilteringInputBuffer(InputStream inputStream) {
+ public ProguardMapReaderWithFilteringInputBuffer(
+ InputStream inputStream, Set<String> classNamesOfInterest) {
+ super(classNamesOfInterest);
this.inputStream = inputStream;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
index cb6991e..a5c1dd6 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
@@ -64,17 +64,16 @@
@Override
public ProguardMappingProvider build() {
try {
+ Set<String> buildForClass = allowLookupAllClasses ? null : allowedLookup;
LineReader reader =
proguardMapProducer.isFileBacked()
- ? new ProguardMapReaderWithFilteringMappedBuffer(proguardMapProducer.getPath())
- : new ProguardMapReaderWithFilteringInputBuffer(proguardMapProducer.get());
+ ? new ProguardMapReaderWithFilteringMappedBuffer(
+ proguardMapProducer.getPath(), buildForClass)
+ : new ProguardMapReaderWithFilteringInputBuffer(
+ proguardMapProducer.get(), buildForClass);
return new ProguardMappingProviderImpl(
ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader,
- diagnosticsHandler,
- true,
- allowExperimental,
- allowLookupAllClasses ? null : allowedLookup));
+ reader, diagnosticsHandler, true, allowExperimental, buildForClass));
} catch (Exception e) {
throw new InvalidMappingFileException(e);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
index eb1d9f3..f5943b5 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
@@ -40,11 +40,9 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
- // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt
- + ".throwsException(KotlinJavaSourceFileTestLibrary.java:22)",
+ + ".throwsException(KotlinJavaSourceFileTestLibrary.kt:22)",
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
- // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt
- + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.java:19)",
+ + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.kt:19)",
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ ".main(KotlinJavaSourceFileTestObject.java:32)");
}
@@ -53,11 +51,9 @@
public List<String> retraceVerboseStackTrace() {
return Arrays.asList(
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
- // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt
- + ".void throwsException()(KotlinJavaSourceFileTestLibrary.java:22)",
+ + ".void throwsException()(KotlinJavaSourceFileTestLibrary.kt:22)",
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
- // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt
- + ".void callsThrowsException()(KotlinJavaSourceFileTestLibrary.java:19)",
+ + ".void callsThrowsException()(KotlinJavaSourceFileTestLibrary.kt:19)",
" at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ ".void main(java.lang.String[])(KotlinJavaSourceFileTestObject.java:32)");
}