Version 2.1.68
Align release version of R8 retrace with 4.2 version.
Bug: 163015628
Change-Id: Id593bc4eb3a38b7479e63f3ee48c98cf79f2c847
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 2c3a634..944cc82 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.1.67";
+ public static final String LABEL = "2.1.68";
private Version() {
}
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 e98d9b9..43a0511 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.naming.ClassNameMapper.MissingFileAction.MISSING_FILE_IS_ERROR;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -16,6 +17,7 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
@@ -68,7 +70,7 @@
public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
return mapperFromBufferedReader(
- new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)));
+ new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)), null);
}
public static ClassNameMapper mapperFromFile(Path path) throws IOException {
@@ -87,12 +89,20 @@
}
public static ClassNameMapper mapperFromString(String contents) throws IOException {
- return mapperFromBufferedReader(CharSource.wrap(contents).openBufferedStream());
+ return mapperFromBufferedReader(CharSource.wrap(contents).openBufferedStream(), null);
}
- private static ClassNameMapper mapperFromBufferedReader(BufferedReader reader)
- throws IOException {
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ public static ClassNameMapper mapperFromString(
+ String contents, DiagnosticsHandler diagnosticsHandler) throws IOException {
+ return mapperFromBufferedReader(
+ CharSource.wrap(contents).openBufferedStream(), diagnosticsHandler);
+ }
+
+ private static ClassNameMapper mapperFromBufferedReader(
+ BufferedReader reader, DiagnosticsHandler diagnosticsHandler) throws IOException {
+ try (ProguardMapReader proguardReader =
+ new ProguardMapReader(
+ reader, diagnosticsHandler != null ? diagnosticsHandler : new Reporter())) {
ClassNameMapper.Builder builder = ClassNameMapper.builder();
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 1404387..5a282d0 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.utils.ThrowingConsumer;
/**
@@ -14,8 +16,16 @@
public interface ClassNaming {
abstract class Builder {
+
public abstract Builder addMemberEntry(MemberNaming entry);
+ public abstract Builder addMappingInformation(MappingInformation mappingInformation);
+
+ public abstract Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber);
+
public abstract ClassNaming build();
/** This is an optional method, may be implemented as no-op */
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index 65bc602..acbb577 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -10,6 +11,7 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -35,6 +37,7 @@
public class ClassNamingForMapApplier implements ClassNaming {
public static class Builder extends ClassNaming.Builder {
+
private final String originalName;
private final String renamedName;
private final Position position;
@@ -75,6 +78,21 @@
}
@Override
+ public ClassNaming.Builder addMappingInformation(MappingInformation mappingInformation) {
+ // Intentionally kept empty until we support additional information with -applymapping.
+ return this;
+ }
+
+ @Override
+ public ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber) {
+ // Intentionally kept empty until we support additional information with -applymapping.
+ return this;
+ }
+
+ @Override
public ClassNamingForMapApplier build() {
return new ClassNamingForMapApplier(
renamedName, originalName, position, qualifiedMethodMembers, methodMembers, fieldMembers);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 5a00fcb..98293ab 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.naming.MemberNaming.NoSignature.NO_SIGNATURE;
+
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
@@ -19,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* Stores name information for a class.
@@ -28,12 +34,14 @@
public class ClassNamingForNameMapper implements ClassNaming {
public static class Builder extends ClassNaming.Builder {
+
private final String originalName;
private final String renamedName;
private final Map<MethodSignature, MemberNaming> methodMembers = Maps.newHashMap();
private final Map<FieldSignature, MemberNaming> fieldMembers = Maps.newHashMap();
private final Map<String, List<MappedRange>> mappedRangesByName = Maps.newHashMap();
private final Map<String, List<MemberNaming>> mappedNamingsByName = Maps.newHashMap();
+ private final Map<Signature, List<MappingInformation>> additionalMappings = Maps.newHashMap();
private Builder(String renamedName, String originalName) {
this.originalName = originalName;
@@ -43,9 +51,9 @@
@Override
public ClassNaming.Builder addMemberEntry(MemberNaming entry) {
if (entry.isMethodNaming()) {
- methodMembers.put((MethodSignature) entry.getRenamedSignature(), entry);
+ methodMembers.put(entry.getRenamedSignature().asMethodSignature(), entry);
} else {
- fieldMembers.put((FieldSignature) entry.getRenamedSignature(), entry);
+ fieldMembers.put(entry.getRenamedSignature().asFieldSignature(), entry);
}
mappedNamingsByName
.computeIfAbsent(entry.getRenamedName(), m -> new ArrayList<>())
@@ -54,6 +62,45 @@
}
@Override
+ public ClassNaming.Builder addMappingInformation(MappingInformation mappingInformation) {
+ return addMappingInformation(
+ mappingInformation,
+ other -> {
+ assert false;
+ });
+ }
+
+ @Override
+ public ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber) {
+ return addMappingInformation(
+ mappingInformation,
+ other ->
+ diagnosticsHandler.warning(
+ MappingInformationDiagnostics.notAllowedCombination(
+ originalName, renamedName, mappingInformation, other, lineNumber)));
+ }
+
+ private ClassNaming.Builder addMappingInformation(
+ MappingInformation mappingInformation, Consumer<MappingInformation> notAllowedCombination) {
+ Signature signature =
+ mappingInformation.isSignatureMappingInformation()
+ ? mappingInformation.asSignatureMappingInformation().getSignature()
+ : NO_SIGNATURE;
+ List<MappingInformation> additionalMappingForSignature =
+ additionalMappings.computeIfAbsent(signature, ignored -> new ArrayList<>());
+ for (MappingInformation information : additionalMappingForSignature) {
+ if (!information.allowOther(mappingInformation)) {
+ notAllowedCombination.accept(information);
+ }
+ }
+ additionalMappingForSignature.add(mappingInformation);
+ return this;
+ }
+
+ @Override
public ClassNamingForNameMapper build() {
Map<String, MappedRangesOfName> map;
@@ -67,7 +114,13 @@
}
return new ClassNamingForNameMapper(
- renamedName, originalName, methodMembers, fieldMembers, map, mappedNamingsByName);
+ renamedName,
+ originalName,
+ methodMembers,
+ fieldMembers,
+ map,
+ mappedNamingsByName,
+ additionalMappings);
}
/** The parameters are forwarded to MappedRange constructor, see explanation there. */
@@ -198,19 +251,23 @@
public final Map<String, List<MemberNaming>> mappedNamingsByName;
+ private final Map<Signature, List<MappingInformation>> additionalMappings;
+
private ClassNamingForNameMapper(
String renamedName,
String originalName,
Map<MethodSignature, MemberNaming> methodMembers,
Map<FieldSignature, MemberNaming> fieldMembers,
Map<String, MappedRangesOfName> mappedRangesByRenamedName,
- Map<String, List<MemberNaming>> mappedNamingsByName) {
+ Map<String, List<MemberNaming>> mappedNamingsByName,
+ Map<Signature, List<MappingInformation>> additionalMappings) {
this.renamedName = renamedName;
this.originalName = originalName;
this.methodMembers = ImmutableMap.copyOf(methodMembers);
this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
this.mappedRangesByRenamedName = mappedRangesByRenamedName;
this.mappedNamingsByName = mappedNamingsByName;
+ this.additionalMappings = additionalMappings;
}
public MappedRangesOfName getMappedRangesForRenamedName(String renamedName) {
@@ -298,7 +355,16 @@
void write(ChainableStringConsumer consumer) {
consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
- // First print field member namings.
+ // Print all additional mapping information.
+ additionalMappings.forEach(
+ (signature, mappingInformations) -> {
+ assert !mappingInformations.isEmpty();
+ for (MappingInformation mappingInformation : mappingInformations) {
+ consumer.accept("# " + mappingInformation.serialize()).accept("\n");
+ }
+ });
+
+ // Print field member namings.
forAllFieldNaming(m -> consumer.accept(" ").accept(m.toString()).accept("\n"));
// Sort MappedRanges by sequence number to restore construction order (original Proguard-map
@@ -313,6 +379,10 @@
}
}
+ public Map<Signature, List<MappingInformation>> getAdditionalMappings() {
+ return additionalMappings;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 84f726d..51c3272 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -58,12 +58,12 @@
final Position position;
public MemberNaming(Signature signature, String renamedName) {
- this(signature, renamedName, Position.UNKNOWN);
+ this(signature, signature.asRenamed(renamedName), Position.UNKNOWN);
}
- public MemberNaming(Signature signature, String renamedName, Position position) {
+ public MemberNaming(Signature signature, Signature renamedSignature, Position position) {
this.signature = signature;
- this.renamedSignature = signature.asRenamed(renamedName);
+ this.renamedSignature = renamedSignature;
this.position = position;
}
@@ -168,6 +168,40 @@
}
}
+ public static class NoSignature extends Signature {
+
+ public static final NoSignature NO_SIGNATURE = new NoSignature();
+
+ public NoSignature() {
+ super("NO SIGNATURE");
+ }
+
+ @Override
+ Signature asRenamed(String renamedName) {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+
+ @Override
+ public SignatureKind kind() {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o == this;
+ }
+
+ @Override
+ public int hashCode() {
+ return 7;
+ }
+
+ @Override
+ void write(Writer builder) throws IOException {
+ throw new Unreachable("Should not be called on NoSignature");
+ }
+ }
+
public static class FieldSignature extends Signature {
public final String 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 747423f..23a9a06 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -3,17 +3,24 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.SignatureMappingInformation;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Maps;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -54,16 +61,19 @@
public class ProguardMapReader implements AutoCloseable {
private final BufferedReader reader;
+ private final JsonParser jsonParser = new JsonParser();
+ private final DiagnosticsHandler diagnosticsHandler;
@Override
public void close() throws IOException {
- if (reader != null) {
- reader.close();
- }
+ reader.close();
}
- ProguardMapReader(BufferedReader reader) {
+ ProguardMapReader(BufferedReader reader, DiagnosticsHandler diagnosticsHandler) {
this.reader = reader;
+ this.diagnosticsHandler = diagnosticsHandler;
+ assert reader != null;
+ assert diagnosticsHandler != null;
}
// Internal parser state
@@ -118,7 +128,7 @@
for (int i = 0; i < line.length(); ++i) {
char c = line.charAt(i);
if (c == '#') {
- return true;
+ return !hasFirstCharJsonBrace(line, i);
} else if (!StringUtils.isWhitespace(c)) {
return false;
}
@@ -126,6 +136,33 @@
return true;
}
+ private boolean isCommentLineWithJsonBrace() {
+ if (line == null) {
+ return false;
+ }
+ for (int i = 0; i < line.length(); ++i) {
+ char c = line.charAt(i);
+ if (c == '#') {
+ return hasFirstCharJsonBrace(line, i);
+ } else if (!Character.isWhitespace(c)) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasFirstCharJsonBrace(String line, int commentCharIndex) {
+ for (int i = commentCharIndex + 1; i < line.length(); i++) {
+ char c = line.charAt(i);
+ if (c == '{') {
+ return true;
+ } else if (!Character.isWhitespace(c)) {
+ return false;
+ }
+ }
+ return false;
+ }
+
private boolean skipLine() throws IOException {
lineOffset = 0;
do {
@@ -213,10 +250,26 @@
MemberNaming lastAddedNaming = null;
MemberNaming activeMemberNaming = null;
Range previousMappedRange = null;
+ Map<Signature, SignatureMappingInformation> mappingInformation = Maps.newHashMap();
do {
Object originalRange = null;
Range mappedRange = null;
-
+ // Try to parse any information added in comments above member namings
+ if (isCommentLineWithJsonBrace()) {
+ MappingInformation mappingInfo =
+ MappingInformation.fromJsonObject(parseJsonInComment(), diagnosticsHandler, lineNo);
+ if (mappingInfo != null) {
+ if (mappingInfo.isSignatureMappingInformation()) {
+ SignatureMappingInformation sigMapInfo = mappingInfo.asSignatureMappingInformation();
+ mappingInformation.put(sigMapInfo.getSignature(), sigMapInfo);
+ } else {
+ classNamingBuilder.addMappingInformation(mappingInfo, diagnosticsHandler, lineNo);
+ }
+ }
+ // Skip reading the rest of the line.
+ lineOffset = line.length();
+ continue;
+ }
// Parse the member line ' x:y:name:z:q -> renamedName'.
if (!StringUtils.isWhitespace(peekCodePoint())) {
break;
@@ -274,7 +327,16 @@
}
}
}
- activeMemberNaming = new MemberNaming(signature, renamedName, getPosition());
+ if (mappingInformation.containsKey(signature)) {
+ activeMemberNaming =
+ new MemberNaming(
+ signature,
+ mappingInformation.get(signature).apply(signature, renamedName, diagnosticsHandler),
+ getPosition());
+ } else {
+ activeMemberNaming =
+ new MemberNaming(signature, signature.asRenamed(renamedName), getPosition());
+ }
previousMappedRange = mappedRange;
} while (nextLine());
@@ -451,6 +513,20 @@
return result;
}
+ private JsonObject parseJsonInComment() {
+ assert isCommentLineWithJsonBrace();
+ try {
+ int firstIndex = 0;
+ while (line.charAt(firstIndex) != '{') {
+ firstIndex++;
+ }
+ return jsonParser.parse(line.substring(firstIndex)).getAsJsonObject();
+ } catch (com.google.gson.JsonSyntaxException ex) {
+ // An info message is reported in MappingInformation.
+ return null;
+ }
+ }
+
private class ParseException extends RuntimeException {
private final int lineNo;
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index be69416..5c50c06 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -74,7 +74,7 @@
private static SeedMapper seedMapperFromInputStream(Reporter reporter, InputStream in)
throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter)) {
SeedMapper.Builder builder = SeedMapper.builder(reporter);
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
new file mode 100644
index 0000000..87b3e7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
@@ -0,0 +1,70 @@
+// 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.naming.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+public class FileNameInformation extends MappingInformation {
+
+ private final String fileName;
+
+ public static final String ID = "sourceFile";
+ static final String FILE_NAME_KEY = "fileName";
+
+ private FileNameInformation(String fileName) {
+ super(NO_LINE_NUMBER);
+ this.fileName = fileName;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ @Override
+ public String serialize() {
+ JsonObject result = new JsonObject();
+ result.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+ result.add(FILE_NAME_KEY, new JsonPrimitive(fileName));
+ return result.toString();
+ }
+
+ @Override
+ public boolean isFileNameInformation() {
+ return true;
+ }
+
+ @Override
+ public FileNameInformation asFileNameInformation() {
+ return this;
+ }
+
+ @Override
+ public boolean allowOther(MappingInformation information) {
+ return !information.isFileNameInformation();
+ }
+
+ public static FileNameInformation build(String fileName) {
+ return new FileNameInformation(fileName);
+ }
+
+ public static FileNameInformation build(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ try {
+ JsonElement fileName =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, FILE_NAME_KEY, ID);
+ if (fileName == null) {
+ return null;
+ }
+ return new FileNameInformation(fileName.getAsString());
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.invalidValueForObjectWithId(lineNumber, FILE_NAME_KEY, ID));
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
new file mode 100644
index 0000000..c1c5105
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -0,0 +1,97 @@
+// 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.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public abstract class MappingInformation {
+
+ static final int NO_LINE_NUMBER = -1;
+
+ public static final String MAPPING_ID_KEY = "id";
+
+ private final int lineNumber;
+
+ MappingInformation(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public abstract String serialize();
+
+ public boolean isSignatureMappingInformation() {
+ return false;
+ }
+
+ public SignatureMappingInformation asSignatureMappingInformation() {
+ return null;
+ }
+
+ public boolean isFileNameInformation() {
+ return false;
+ }
+
+ public FileNameInformation asFileNameInformation() {
+ return null;
+ }
+
+ public boolean isMethodSignatureChangedInformation() {
+ return false;
+ }
+
+ public MethodSignatureChangedInformation asMethodSignatureChangedInformation() {
+ return null;
+ }
+
+ public abstract boolean allowOther(MappingInformation information);
+
+ public static MappingInformation fromJsonObject(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ if (object == null) {
+ diagnosticsHandler.info(MappingInformationDiagnostics.notValidJson(lineNumber));
+ return null;
+ }
+ JsonElement id = object.get(MAPPING_ID_KEY);
+ if (id == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.noKeyInJson(lineNumber, MAPPING_ID_KEY));
+ return null;
+ }
+ String idString = id.getAsString();
+ if (idString == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.notValidString(lineNumber, MAPPING_ID_KEY));
+ return null;
+ }
+ switch (idString) {
+ case MethodSignatureChangedInformation.ID:
+ return MethodSignatureChangedInformation.build(object, diagnosticsHandler, lineNumber);
+ case FileNameInformation.ID:
+ return FileNameInformation.build(object, diagnosticsHandler, lineNumber);
+ default:
+ diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, idString));
+ return null;
+ }
+ }
+
+ static JsonElement getJsonElementFromObject(
+ JsonObject object,
+ DiagnosticsHandler diagnosticsHandler,
+ int lineNumber,
+ String key,
+ String id) {
+ JsonElement element = object.get(key);
+ if (element == null) {
+ diagnosticsHandler.info(
+ MappingInformationDiagnostics.noKeyForObjectWithId(lineNumber, key, MAPPING_ID_KEY, id));
+ }
+ return element;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
new file mode 100644
index 0000000..6cc0b28
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformationDiagnostics.java
@@ -0,0 +1,113 @@
+// 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.mappinginformation;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+
+@Keep
+public class MappingInformationDiagnostics implements Diagnostic {
+
+ private final String message;
+ private final Position position;
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return message;
+ }
+
+ private MappingInformationDiagnostics(String message, Position position) {
+ this.message = message;
+ this.position = position;
+ }
+
+ static MappingInformationDiagnostics noHandlerFor(int lineNumber, String value) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not find a handler for %s", value),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics noKeyInJson(int lineNumber, String key) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not locate '%s' in the JSON object", key),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics notValidJson(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Not valid JSON", new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics notValidString(int lineNumber, String key) {
+ return new MappingInformationDiagnostics(
+ String.format("The value of '%s' is not a valid string in the JSON object", key),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics tooManyInformationalParameters(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "More informational parameters than actual parameters for method signature",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics noKeyForObjectWithId(
+ int lineNumber, String key, String mappingKey, String mappingValue) {
+ return new MappingInformationDiagnostics(
+ String.format("Could not find '%s' for object with %s '%s'", key, mappingKey, mappingValue),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics invalidValueForObjectWithId(
+ int lineNumber, String mappingKey, String mappingValue) {
+ return new MappingInformationDiagnostics(
+ String.format(
+ "Could not decode the information for the object with %s '%s'",
+ mappingKey, mappingValue),
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics tooManyEntriesForParameterInformation(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Parameter information do not have 1 or 2 entries",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ static MappingInformationDiagnostics invalidParameterInformationObject(int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "Parameter information is not an index and a string representation of a type",
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+
+ public static MappingInformationDiagnostics notAllowedCombination(
+ String className,
+ String renamedClassName,
+ MappingInformation one,
+ MappingInformation other,
+ int lineNumber) {
+ return new MappingInformationDiagnostics(
+ "The mapping '"
+ + one.serialize()
+ + "' is not allowed in combination with '"
+ + other.serialize()
+ + "' in the mapping for "
+ + className
+ + " -> "
+ + renamedClassName,
+ new TextPosition(1, lineNumber, TextPosition.UNKNOWN_COLUMN));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java
new file mode 100644
index 0000000..8bb023d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MethodSignatureChangedInformation.java
@@ -0,0 +1,258 @@
+// 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.mappinginformation;
+
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.invalidParameterInformationObject;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.invalidValueForObjectWithId;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.tooManyEntriesForParameterInformation;
+import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.tooManyInformationalParameters;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * The MethodSignatureChangedInformation structure adds extra information regarding the mapped
+ * method signature that is otherwise not available in the existing proguard mapping format. The
+ * JSON-structure is as follows:
+ *
+ * <pre>
+ * {
+ * "id": "argumentsChanged",
+ * "signature": { methodSignature },
+ * "returnType": "java.lang.String",
+ * "receiver": false,
+ * "params": [
+ * [1], // <-- parameter with original index 1 (starting index based on receiver) is removed.
+ * [2, Foo] // <-- parameter with index 2 has type Foo
+ * ]
+ * }
+ * </pre>
+ */
+public class MethodSignatureChangedInformation extends SignatureMappingInformation {
+
+ private ParameterInformation[] argumentInfos;
+ private final boolean receiver;
+ private final String returnType;
+ private final MethodSignature signature;
+
+ public static final String ID = "methodSignatureChanged";
+ private static final String RETURN_TYPE_KEY = "returnType";
+ private static final String PARAMS_KEY = "params";
+ private static final String RECEIVER_KEY = "receiver";
+
+ @Override
+ public String serialize() {
+ JsonObject result = new JsonObject();
+ serializeMethodSignature(result, signature);
+ result.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+ result.add(RECEIVER_KEY, new JsonPrimitive(receiver));
+ result.add(RETURN_TYPE_KEY, new JsonPrimitive(returnType));
+ JsonArray arguments = new JsonArray();
+ for (ParameterInformation argInfo : argumentInfos) {
+ arguments.add(argInfo.serialize());
+ }
+ result.add(PARAMS_KEY, arguments);
+ return result.toString();
+ }
+
+ @Override
+ public boolean allowOther(MappingInformation information) {
+ return !information.isMethodSignatureChangedInformation();
+ }
+
+ @Override
+ public Signature getSignature() {
+ return signature;
+ }
+
+ @Override
+ public Signature apply(
+ Signature originalSignature, String renamedName, DiagnosticsHandler diagnosticsHandler) {
+ if (originalSignature == null || !originalSignature.isMethodSignature()) {
+ assert false : "Should only call apply for method signature";
+ return originalSignature;
+ }
+ MethodSignature signature = originalSignature.asMethodSignature();
+ String type = signature.type;
+ String[] parameters = signature.parameters;
+ int numberOfArgumentsRemoved = getNumberOfArgumentsRemoved();
+ if (numberOfArgumentsRemoved > parameters.length) {
+ // The mapping information is not up to date with the current signature.
+ diagnosticsHandler.warning(tooManyInformationalParameters(getLineNumber()));
+ return new MethodSignature(renamedName, type, parameters);
+ }
+ String[] newParameters = new String[parameters.length - numberOfArgumentsRemoved];
+ int insertIndex = 0;
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterInformation argInfo = getParameterInformation(i);
+ if (argInfo != null && argInfo.getType() == null) {
+ // Argument has been removed.
+ } else {
+ if (insertIndex >= newParameters.length) {
+ // The mapping information is not up to date with the current signature.
+ diagnosticsHandler.warning(tooManyInformationalParameters(getLineNumber()));
+ return new MethodSignature(renamedName, type, parameters);
+ } else if (argInfo == null) {
+ // Unchanged, take current parameter.
+ newParameters[insertIndex++] = parameters[i];
+ } else {
+ newParameters[insertIndex++] = argInfo.getType();
+ }
+ }
+ }
+ assert insertIndex == newParameters.length;
+ return new MethodSignature(renamedName, getReturnType(), newParameters);
+ }
+
+ @Override
+ public boolean isMethodSignatureChangedInformation() {
+ return true;
+ }
+
+ public int getNumberOfArgumentsRemoved() {
+ int removedCount = 0;
+ for (ParameterInformation argInfo : argumentInfos) {
+ if (argInfo.type == null) {
+ removedCount++;
+ }
+ }
+ return removedCount;
+ }
+
+ public boolean hasReceiver() {
+ return receiver;
+ }
+
+ public String getReturnType() {
+ return returnType;
+ }
+
+ public ParameterInformation getParameterInformation(int index) {
+ int subtractIndex = receiver ? 1 : 0;
+ for (int i = 0; i < argumentInfos.length; i++) {
+ if (argumentInfos[i].index - subtractIndex == index) {
+ return argumentInfos[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public MethodSignatureChangedInformation asMethodSignatureChangedInformation() {
+ return this;
+ }
+
+ private MethodSignatureChangedInformation(
+ MethodSignature signature,
+ String returnType,
+ boolean hasReceiver,
+ ParameterInformation[] argumentInfos,
+ int lineNumber) {
+ super(lineNumber);
+ this.signature = signature;
+ this.argumentInfos = argumentInfos;
+ this.returnType = returnType;
+ this.receiver = hasReceiver;
+ }
+
+ public static MappingInformation build(
+ JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ try {
+ JsonElement returnTypeElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, RETURN_TYPE_KEY, ID);
+ JsonElement receiverElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, RECEIVER_KEY, ID);
+ JsonElement argsElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, PARAMS_KEY, ID);
+ MethodSignature signature = getMethodSignature(object, ID, diagnosticsHandler, lineNumber);
+ if (signature == null
+ || returnTypeElement == null
+ || receiverElement == null
+ || argsElement == null) {
+ return null;
+ }
+ JsonArray argumentsArray = argsElement.getAsJsonArray();
+ if (argumentsArray == null) {
+ return null;
+ }
+ ParameterInformation[] args = new ParameterInformation[argumentsArray.size()];
+ for (int i = 0; i < argumentsArray.size(); i++) {
+ args[i] =
+ ParameterInformation.fromJsonArray(
+ argumentsArray.get(i).getAsJsonArray(), diagnosticsHandler, lineNumber);
+ }
+ return new MethodSignatureChangedInformation(
+ signature,
+ returnTypeElement.getAsString(),
+ receiverElement.getAsBoolean(),
+ args,
+ lineNumber);
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(invalidValueForObjectWithId(lineNumber, MAPPING_ID_KEY, ID));
+ return null;
+ }
+ }
+
+ public static class ParameterInformation {
+ private final int index;
+ private final String type;
+
+ public int getIndex() {
+ return index;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ private ParameterInformation(int index, String type) {
+ this.index = index;
+ this.type = type;
+ }
+
+ static ParameterInformation fromJsonArray(
+ JsonArray argumentInfo, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ assert argumentInfo != null;
+ try {
+ if (argumentInfo.size() > 2) {
+ diagnosticsHandler.info(tooManyEntriesForParameterInformation(lineNumber));
+ return null;
+ }
+ int index = argumentInfo.get(0).getAsInt();
+ if (argumentInfo.size() == 1) {
+ // This is a removed argument - no type information
+ return new ParameterInformation(index, null);
+ } else {
+ return new ParameterInformation(index, argumentInfo.get(1).getAsString());
+ }
+ } catch (UnsupportedOperationException | IllegalStateException ignored) {
+ diagnosticsHandler.info(invalidParameterInformationObject(lineNumber));
+ return null;
+ }
+ }
+
+ public static ParameterInformation buildRemovedParameterInformation(int index) {
+ return new ParameterInformation(index, null);
+ }
+
+ public static ParameterInformation buildChangedParameterInformation(int index, String type) {
+ return new ParameterInformation(index, type);
+ }
+
+ JsonArray serialize() {
+ JsonArray serializedArray = new JsonArray();
+ serializedArray.add(index);
+ if (type != null) {
+ serializedArray.add(type);
+ }
+ return serializedArray;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java
new file mode 100644
index 0000000..aba33bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/SignatureMappingInformation.java
@@ -0,0 +1,64 @@
+// 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.naming.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public abstract class SignatureMappingInformation extends MappingInformation {
+
+ private static final String SIGNATURE_KEY = "signature";
+
+ SignatureMappingInformation(int lineNumber) {
+ super(lineNumber);
+ }
+
+ @Override
+ public boolean isSignatureMappingInformation() {
+ return true;
+ }
+
+ @Override
+ public SignatureMappingInformation asSignatureMappingInformation() {
+ return this;
+ }
+
+ public abstract Signature getSignature();
+
+ public abstract Signature apply(
+ Signature originalSignature, String renamedName, DiagnosticsHandler diagnosticsHandler);
+
+ JsonObject serializeMethodSignature(JsonObject object, MethodSignature signature) {
+ JsonArray signatureArr = new JsonArray();
+ signatureArr.add(signature.type);
+ signatureArr.add(signature.name);
+ for (String parameter : signature.parameters) {
+ signatureArr.add(parameter);
+ }
+ object.add(SIGNATURE_KEY, signatureArr);
+ return object;
+ }
+
+ static MethodSignature getMethodSignature(
+ JsonObject object, String id, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+ JsonElement signatureElement =
+ getJsonElementFromObject(object, diagnosticsHandler, lineNumber, SIGNATURE_KEY, id);
+ if (signatureElement == null || !signatureElement.isJsonArray()) {
+ return null;
+ }
+ // Signature will be [returnType, name, param1, param2, ...].
+ JsonArray signature = signatureElement.getAsJsonArray();
+ String[] parameters = new String[signature.size() - 2];
+ for (int i = 2; i < signature.size(); i++) {
+ parameters[i - 2] = signature.get(i).getAsString();
+ }
+ return new MethodSignature(
+ signature.get(1).getAsString(), signature.get(0).getAsString(), parameters);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 0edcea8..f34a5aa 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.Version;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.retrace.RetraceCommand.Builder;
import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
@@ -17,7 +18,11 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Charsets;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -35,9 +40,16 @@
@Keep
public class Retrace {
+ // This is a slight modification of the default regular expression shown for proguard retrace
+ // that allow for retracing classes in the form <class>: lorem ipsum...
+ // Seems like Proguard retrace is expecting the form "Caused by: <class>".
+ public static final String DEFAULT_REGULAR_EXPRESSION =
+ "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)"
+ + "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
+
public static final String USAGE_MESSAGE =
StringUtils.lines(
- "Usage: retrace <proguard-map> <stacktrace-file> [--regex <regexp>, --verbose, --info]",
+ "Usage: retrace <proguard-map> [stack-trace-file] [--regex <regexp>, --verbose, --info]",
" where <proguard-map> is an r8 generated mapping file.");
private static Builder parseArguments(String[] args, DiagnosticsHandler diagnosticsHandler) {
@@ -45,11 +57,16 @@
Builder builder = RetraceCommand.builder(diagnosticsHandler);
boolean hasSetProguardMap = false;
boolean hasSetStackTrace = false;
+ boolean hasSetRegularExpression = false;
while (context.head() != null) {
Boolean help = OptionsParsing.tryParseBoolean(context, "--help");
if (help != null) {
return null;
}
+ Boolean version = OptionsParsing.tryParseBoolean(context, "--version");
+ if (version != null) {
+ return null;
+ }
Boolean info = OptionsParsing.tryParseBoolean(context, "--info");
if (info != null) {
// This is already set in the diagnostics handler.
@@ -63,6 +80,7 @@
String regex = OptionsParsing.tryParseSingle(context, "--regex", "r");
if (regex != null && !regex.isEmpty()) {
builder.setRegularExpression(regex);
+ hasSetRegularExpression = true;
continue;
}
if (!hasSetProguardMap) {
@@ -88,6 +106,9 @@
if (!hasSetStackTrace) {
builder.setStackTrace(getStackTraceFromStandardInput());
}
+ if (!hasSetRegularExpression) {
+ builder.setRegularExpression(DEFAULT_REGULAR_EXPRESSION);
+ }
return builder;
}
@@ -105,7 +126,7 @@
private static List<String> getStackTraceFromFile(
String stackTracePath, DiagnosticsHandler diagnostics) {
try {
- return Files.readAllLines(Paths.get(stackTracePath));
+ return Files.readAllLines(Paths.get(stackTracePath), Charsets.UTF_8);
} catch (IOException e) {
diagnostics.error(new StringDiagnostic("Could not find stack trace file: " + stackTracePath));
throw new RetraceAbortException();
@@ -122,23 +143,25 @@
Timing timing = Timing.create("R8 retrace", command.printMemory());
timing.begin("Read proguard map");
ClassNameMapper classNameMapper =
- ClassNameMapper.mapperFromString(command.proguardMapProducer.get());
+ ClassNameMapper.mapperFromString(
+ command.proguardMapProducer.get(), command.diagnosticsHandler);
timing.end();
- RetraceBase retraceBase = RetraceBaseImpl.create(classNameMapper);
+ RetraceApi retracer = Retracer.create(classNameMapper);
RetraceCommandLineResult result;
timing.begin("Parse and Retrace");
if (command.regularExpression != null) {
result =
new RetraceRegularExpression(
- retraceBase,
+ retracer,
command.stackTrace,
command.diagnosticsHandler,
- command.regularExpression)
+ command.regularExpression,
+ command.isVerbose)
.retrace();
} else {
result =
new RetraceStackTrace(
- retraceBase, command.stackTrace, command.diagnosticsHandler, command.isVerbose)
+ retracer, command.stackTrace, command.diagnosticsHandler, command.isVerbose)
.retrace();
}
timing.end();
@@ -156,18 +179,47 @@
}
public static void run(String[] args) {
+ // To be compatible with standard retrace and remapper, we translate -arg into --arg.
+ String[] mappedArgs = new String[args.length];
+ boolean printInfo = false;
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg == null || arg.length() < 2) {
+ mappedArgs[i] = arg;
+ continue;
+ }
+ if (arg.charAt(0) == '-' && arg.charAt(1) != '-') {
+ mappedArgs[i] = "-" + arg;
+ } else {
+ mappedArgs[i] = arg;
+ }
+ if (mappedArgs[i].equals("--info")) {
+ printInfo = true;
+ }
+ }
RetraceDiagnosticsHandler retraceDiagnosticsHandler =
- new RetraceDiagnosticsHandler(
- new DiagnosticsHandler() {}, Arrays.asList(args).contains("--info"));
- Builder builder = parseArguments(args, retraceDiagnosticsHandler);
+ new RetraceDiagnosticsHandler(new DiagnosticsHandler() {}, printInfo);
+ Builder builder = parseArguments(mappedArgs, retraceDiagnosticsHandler);
if (builder == null) {
- // --help was an argument to list
- assert Arrays.asList(args).contains("--help");
+ // --help or --version was an argument to list
+ if (Arrays.asList(mappedArgs).contains("--version")) {
+ System.out.println("Retrace " + Version.getVersionString());
+ return;
+ }
+ assert Arrays.asList(mappedArgs).contains("--help");
System.out.print(USAGE_MESSAGE);
return;
}
builder.setRetracedStackTraceConsumer(
- retraced -> System.out.print(StringUtils.lines(retraced)));
+ retraced -> {
+ try (PrintStream printStream = new PrintStream(System.out, true, Charsets.UTF_8.name())) {
+ for (String line : retraced) {
+ printStream.println(line);
+ }
+ } catch (UnsupportedEncodingException e) {
+ retraceDiagnosticsHandler.error(new StringDiagnostic(e.getMessage()));
+ }
+ });
run(builder.build());
}
@@ -181,7 +233,7 @@
}
private static List<String> getStackTraceFromStandardInput() {
- Scanner sc = new Scanner(System.in);
+ Scanner sc = new Scanner(new InputStreamReader(System.in, Charsets.UTF_8));
List<String> readLines = new ArrayList<>();
while (sc.hasNext()) {
readLines.add(sc.nextLine());
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceApi.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/retrace/RetraceBase.java
rename to src/main/java/com/android/tools/r8/retrace/RetraceApi.java
index 2874fc8..7bb9232 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceApi.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
-public interface RetraceBase {
+/** This is the main api interface for retrace. */
+@Keep
+public interface RetraceApi {
RetraceMethodResult retrace(MethodReference methodReference);
@@ -18,12 +21,4 @@
RetraceClassResult retrace(ClassReference classReference);
RetraceTypeResult retrace(TypeReference typeReference);
-
- String retraceSourceFile(ClassReference classReference, String sourceFile);
-
- String retraceSourceFile(
- ClassReference classReference,
- String sourceFile,
- ClassReference retracedClassReference,
- boolean hasRetraceResult);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
deleted file mode 100644
index 9a68706..0000000
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
+++ /dev/null
@@ -1,98 +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.retrace;
-
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.utils.Box;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
-import java.util.Set;
-
-public class RetraceBaseImpl implements RetraceBase {
-
- private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
- Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
-
- private final ClassNameMapper classNameMapper;
-
- private RetraceBaseImpl(ClassNameMapper classNameMapper) {
- this.classNameMapper = classNameMapper;
- }
-
- public static RetraceBase create(ClassNameMapper classNameMapper) {
- return new RetraceBaseImpl(classNameMapper);
- }
-
- @Override
- public RetraceMethodResult retrace(MethodReference methodReference) {
- return retrace(methodReference.getHolderClass()).lookupMethod(methodReference.getMethodName());
- }
-
- @Override
- public RetraceFieldResult retrace(FieldReference fieldReference) {
- return retrace(fieldReference.getHolderClass()).lookupField(fieldReference.getFieldName());
- }
-
- @Override
- public RetraceClassResult retrace(ClassReference classReference) {
- return RetraceClassResult.create(
- classReference, classNameMapper.getClassNaming(classReference.getTypeName()));
- }
-
- @Override
- public String retraceSourceFile(ClassReference classReference, String sourceFile) {
- Box<String> retracedSourceFile = new Box<>();
- retrace(classReference)
- .forEach(element -> retracedSourceFile.set(element.retraceSourceFile(sourceFile, this)));
- return retracedSourceFile.get();
- }
-
- @Override
- public RetraceTypeResult retrace(TypeReference typeReference) {
- return new RetraceTypeResult(typeReference, this);
- }
-
- @Override
- public String retraceSourceFile(
- ClassReference obfuscatedClass,
- String sourceFile,
- ClassReference retracedClassReference,
- boolean hasRetraceResult) {
- boolean fileNameProbablyChanged =
- hasRetraceResult
- && !retracedClassReference.getTypeName().startsWith(obfuscatedClass.getTypeName());
- if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
- // We have no new information, only rewrite filename if it is unknown.
- // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
- return sourceFile;
- }
- if (!hasRetraceResult) {
- // We have no mapping but but the file name is unknown, so the best we can do is take the
- // name of the obfuscated clazz.
- assert obfuscatedClass.getTypeName().equals(retracedClassReference.getTypeName());
- return getClassSimpleName(obfuscatedClass.getTypeName()) + ".java";
- }
- String newFileName = getClassSimpleName(retracedClassReference.getTypeName());
- String extension = Files.getFileExtension(sourceFile);
- if (extension.isEmpty()) {
- extension = "java";
- }
- return newFileName + "." + extension;
- }
-
- private static String getClassSimpleName(String clazz) {
- int lastIndexOfPeriod = clazz.lastIndexOf('.');
- // Check if we can find a subclass separator.
- int endIndex = clazz.lastIndexOf('$');
- if (lastIndexOfPeriod > endIndex || endIndex < 0) {
- endIndex = clazz.length();
- }
- return clazz.substring(lastIndexOfPeriod + 1, endIndex);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index 25505fe..e13c832 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.naming.MemberNaming.NoSignature.NO_SIGNATURE;
+import static com.android.tools.r8.retrace.RetraceUtils.synthesizeFileName;
+
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceClassResult.Element;
@@ -22,15 +26,18 @@
private final ClassReference obfuscatedReference;
private final ClassNamingForNameMapper mapper;
+ private final RetraceApi retracer;
- private RetraceClassResult(ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
+ private RetraceClassResult(
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetraceApi retracer) {
this.obfuscatedReference = obfuscatedReference;
this.mapper = mapper;
+ this.retracer = retracer;
}
static RetraceClassResult create(
- ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
- return new RetraceClassResult(obfuscatedReference, mapper);
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetraceApi retracer) {
+ return new RetraceClassResult(obfuscatedReference, mapper, retracer);
}
public RetraceFieldResult lookupField(String fieldName) {
@@ -71,12 +78,12 @@
if (element.mapper != null) {
mappedRangesForT = lookupFunction.apply(element.mapper, name);
}
- elementBox.set(constructor.create(element, mappedRangesForT, name));
+ elementBox.set(constructor.create(element, mappedRangesForT, name, retracer));
});
return elementBox.get();
}
- private boolean hasRetraceResult() {
+ boolean hasRetraceResult() {
return mapper != null;
}
@@ -86,7 +93,8 @@
new Element(
this,
mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName),
- mapper));
+ mapper,
+ false));
}
@Override
@@ -96,7 +104,12 @@
}
private interface ResultConstructor<T, R> {
- R create(Element element, T mappings, String obfuscatedName);
+ R create(Element element, T mappings, String obfuscatedName, RetraceApi retraceApi);
+ }
+
+ public boolean isAmbiguous() {
+ // Currently we have no way of producing ambiguous class results.
+ return false;
}
public static class Element {
@@ -108,7 +121,8 @@
public Element(
RetraceClassResult classResult,
ClassReference classReference,
- ClassNamingForNameMapper mapper) {
+ ClassNamingForNameMapper mapper,
+ boolean isAmbiguous) {
this.classResult = classResult;
this.classReference = classReference;
this.mapper = mapper;
@@ -122,9 +136,26 @@
return classResult;
}
- public String retraceSourceFile(String fileName, RetraceBase retraceBase) {
- return retraceBase.retraceSourceFile(
- classResult.obfuscatedReference, fileName, classReference, mapper != null);
+ public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ if (mapper != null && mapper.getAdditionalMappings().size() > 0) {
+ List<MappingInformation> mappingInformations =
+ mapper.getAdditionalMappings().get(NO_SIGNATURE);
+ if (mappingInformations != null) {
+ for (MappingInformation mappingInformation : mappingInformations) {
+ if (mappingInformation.isFileNameInformation()) {
+ return new RetraceSourceFileResult(
+ mappingInformation.asFileNameInformation().getFileName(), false);
+ }
+ }
+ }
+ }
+ return new RetraceSourceFileResult(
+ synthesizeFileName(
+ classReference.getTypeName(),
+ classResult.obfuscatedReference.getTypeName(),
+ sourceFile,
+ mapper != null),
+ true);
}
public RetraceFieldResult lookupField(String fieldName) {
@@ -158,7 +189,10 @@
BiFunction<ClassNamingForNameMapper, String, T> lookupFunction,
ResultConstructor<T, R> constructor) {
return constructor.create(
- this, mapper != null ? lookupFunction.apply(mapper, name) : null, name);
+ this,
+ mapper != null ? lookupFunction.apply(mapper, name) : null,
+ name,
+ classResult.retracer);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
index d02913e..5477eaa 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -23,14 +23,17 @@
private final RetraceClassResult.Element classElement;
private final List<MemberNaming> memberNamings;
private final String obfuscatedName;
+ private final RetraceApi retracer;
RetraceFieldResult(
RetraceClassResult.Element classElement,
List<MemberNaming> memberNamings,
- String obfuscatedName) {
+ String obfuscatedName,
+ RetraceApi retracer) {
this.classElement = classElement;
this.memberNamings = memberNamings;
this.obfuscatedName = obfuscatedName;
+ this.retracer = retracer;
assert classElement != null;
assert memberNamings == null
|| (!memberNamings.isEmpty() && memberNamings.stream().allMatch(Objects::nonNull));
@@ -40,7 +43,7 @@
return memberNamings != null;
}
- private boolean isAmbiguous() {
+ public boolean isAmbiguous() {
if (!hasRetraceResult()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
index f0719ea..7977858 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -26,15 +26,18 @@
private final String obfuscatedName;
private final RetraceClassResult.Element classElement;
private final MappedRangesOfName mappedRanges;
+ private final RetraceApi retracer;
private Boolean isAmbiguousCached = null;
RetraceMethodResult(
RetraceClassResult.Element classElement,
MappedRangesOfName mappedRanges,
- String obfuscatedName) {
+ String obfuscatedName,
+ RetraceApi retracer) {
this.classElement = classElement;
this.mappedRanges = mappedRanges;
this.obfuscatedName = obfuscatedName;
+ this.retracer = retracer;
assert classElement != null;
}
@@ -87,7 +90,7 @@
}
}
return new RetraceMethodResult(
- classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName);
+ classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName, retracer);
}
@Override
@@ -180,5 +183,10 @@
}
return mappedRange.getFirstLineNumberOfOriginalRange();
}
+
+ public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ return RetraceUtils.getSourceFile(
+ classElement, methodReference.getHolderClass(), sourceFile, retraceMethodResult.retracer);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index b561305..40b3f41 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -5,100 +5,120 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceClassResult.Element;
-import com.android.tools.r8.retrace.RetraceRegularExpression.RetraceString.RetraceStringBuilder;
+import com.android.tools.r8.retrace.RetraceRegularExpression.ClassNameGroup.ClassNameGroupHandler;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Set;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class RetraceRegularExpression {
- private final RetraceBase retraceBase;
+ private final RetraceApi retracer;
private final List<String> stackTrace;
private final DiagnosticsHandler diagnosticsHandler;
private final String regularExpression;
private static final int NO_MATCH = -1;
- private final RegularExpressionGroup[] groups =
- new RegularExpressionGroup[] {
- new TypeNameGroup(),
- new BinaryNameGroup(),
- new MethodNameGroup(),
- new FieldNameGroup(),
- new SourceFileGroup(),
- new LineNumberGroup(),
- new FieldOrReturnTypeGroup(),
- new MethodArgumentsGroup()
- };
+ private final SourceFileLineNumberGroup sourceFileLineNumberGroup =
+ new SourceFileLineNumberGroup();
+ private final TypeNameGroup typeNameGroup = new TypeNameGroup();
+ private final BinaryNameGroup binaryNameGroup = new BinaryNameGroup();
+ private final SourceFileGroup sourceFileGroup = new SourceFileGroup();
+ private final LineNumberGroup lineNumberGroup = new LineNumberGroup();
+ private final FieldOrReturnTypeGroup fieldOrReturnTypeGroup = new FieldOrReturnTypeGroup();
+ private final MethodArgumentsGroup methodArgumentsGroup = new MethodArgumentsGroup();
+
+ private final MethodNameGroup methodNameGroup;
+ private final FieldNameGroup fieldNameGroup;
private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
+ private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
RetraceRegularExpression(
- RetraceBase retraceBase,
+ RetraceApi retracer,
List<String> stackTrace,
DiagnosticsHandler diagnosticsHandler,
- String regularExpression) {
- this.retraceBase = retraceBase;
+ String regularExpression,
+ boolean isVerbose) {
+ this.retracer = retracer;
this.stackTrace = stackTrace;
this.diagnosticsHandler = diagnosticsHandler;
this.regularExpression = regularExpression;
+ methodNameGroup = new MethodNameGroup(isVerbose);
+ fieldNameGroup = new FieldNameGroup(isVerbose);
}
public RetraceCommandLineResult retrace() {
List<RegularExpressionGroupHandler> handlers = new ArrayList<>();
- String regularExpression = registerGroups(this.regularExpression, handlers);
- Pattern compiledPattern = Pattern.compile(regularExpression);
+ StringBuilder refinedRegularExpressionBuilder = new StringBuilder();
+ registerGroups(
+ this.regularExpression,
+ refinedRegularExpressionBuilder,
+ handlers,
+ FIRST_CAPTURE_GROUP_INDEX);
+ String refinedRegularExpression = refinedRegularExpressionBuilder.toString();
+ Pattern compiledPattern = Pattern.compile(refinedRegularExpression);
List<String> result = new ArrayList<>();
for (String string : stackTrace) {
Matcher matcher = compiledPattern.matcher(string);
- List<RetraceString> retracedStrings =
- Lists.newArrayList(RetraceStringBuilder.create(string).build());
- if (matcher.matches()) {
- for (RegularExpressionGroupHandler handler : handlers) {
- retracedStrings = handler.handleMatch(retracedStrings, matcher, retraceBase);
- }
+ if (!matcher.matches()) {
+ result.add(string);
+ continue;
+ }
+ // Iterate through handlers to set contexts. That will allow us to process all handlers from
+ // left to right.
+ RetraceStringContext initialContext = RetraceStringContext.empty();
+ for (RegularExpressionGroupHandler handler : handlers) {
+ initialContext = handler.buildInitial(initialContext, matcher, retracer);
+ }
+ final RetraceString initialRetraceString = RetraceString.start(initialContext);
+ List<RetraceString> retracedStrings = Lists.newArrayList(initialRetraceString);
+ for (RegularExpressionGroupHandler handler : handlers) {
+ retracedStrings = handler.handleMatch(string, retracedStrings, matcher, retracer);
}
if (retracedStrings.isEmpty()) {
// We could not find a match. Output the identity.
result.add(string);
- } else {
- boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous;
- if (isAmbiguous) {
- retracedStrings.sort(new RetraceLineComparator());
- }
- ClassReference previousContext = null;
- for (RetraceString retracedString : retracedStrings) {
- String finalString = retracedString.getRetracedString();
- if (!isAmbiguous) {
- result.add(finalString);
- continue;
- }
- assert retracedString.getClassContext() != null;
- ClassReference currentContext = retracedString.getClassContext().getClassReference();
- if (currentContext.equals(previousContext)) {
- int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
- finalString =
- finalString.substring(0, firstNonWhitespaceCharacter)
- + "<OR> "
- + finalString.substring(firstNonWhitespaceCharacter);
- }
- previousContext = currentContext;
+ }
+ boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous();
+ if (isAmbiguous) {
+ retracedStrings.sort(new RetraceLineComparator());
+ }
+ ClassReference previousContext = null;
+ for (RetraceString retracedString : retracedStrings) {
+ String finalString = retracedString.builder.build(string);
+ if (!isAmbiguous) {
result.add(finalString);
+ continue;
}
+ assert retracedString.getClassContext() != null;
+ ClassReference currentContext = retracedString.getClassContext().getClassReference();
+ if (currentContext.equals(previousContext)) {
+ int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
+ finalString =
+ finalString.substring(0, firstNonWhitespaceCharacter)
+ + "<OR> "
+ + finalString.substring(firstNonWhitespaceCharacter);
+ }
+ previousContext = currentContext;
+ result.add(finalString);
}
}
return new RetraceCommandLineResult(result);
@@ -126,273 +146,344 @@
}
}
- private String registerGroups(
- String regularExpression, List<RegularExpressionGroupHandler> handlers) {
- int currentIndex = 0;
- int captureGroupIndex = 0;
- while (currentIndex < regularExpression.length()) {
- RegularExpressionGroup firstGroup = null;
- int firstIndexFromCurrent = regularExpression.length();
- for (RegularExpressionGroup group : groups) {
- int nextIndexOf = regularExpression.indexOf(group.shortName(), currentIndex);
- if (nextIndexOf > NO_MATCH && nextIndexOf < firstIndexFromCurrent) {
- // Check if previous character in the regular expression is not \\ to ensure not
- // overriding a matching on shortName.
- if (nextIndexOf > 0 && regularExpression.charAt(nextIndexOf - 1) == '\\') {
- continue;
+ private int registerGroups(
+ String regularExpression,
+ StringBuilder refinedRegularExpression,
+ List<RegularExpressionGroupHandler> handlers,
+ int captureGroupIndex) {
+ int lastCommittedIndex = 0;
+ RegularExpressionGroupHandler lastHandler = null;
+ boolean seenPercentage = false;
+ boolean escaped = false;
+ for (int i = 0; i < regularExpression.length(); i++) {
+ if (seenPercentage) {
+ assert !escaped;
+ final RegularExpressionGroup group = getGroupFromVariable(regularExpression.charAt(i));
+ refinedRegularExpression.append(regularExpression, lastCommittedIndex, i - 1);
+ if (group.isSynthetic()) {
+ captureGroupIndex =
+ registerGroups(
+ group.subExpression(), refinedRegularExpression, handlers, captureGroupIndex);
+ } else {
+ String captureGroupName = CAPTURE_GROUP_PREFIX + (captureGroupIndex++);
+ refinedRegularExpression
+ .append("(?<")
+ .append(captureGroupName)
+ .append(">")
+ .append(group.subExpression())
+ .append(")");
+ final RegularExpressionGroupHandler handler = group.createHandler(captureGroupName);
+ // If we see a pattern as %c.%m or %C/%m, then register the groups to allow delaying
+ // writing of the class string until we have the fully qualified member.
+ if (lastHandler != null
+ && handler.isQualifiedHandler()
+ && lastHandler.isClassNameGroupHandler()
+ && lastCommittedIndex == i - 3
+ && isTypeOrBinarySeparator(regularExpression, lastCommittedIndex, i - 2)) {
+ final ClassNameGroupHandler classNameGroupHandler =
+ lastHandler.asClassNameGroupHandler();
+ final QualifiedRegularExpressionGroupHandler qualifiedHandler =
+ handler.asQualifiedHandler();
+ classNameGroupHandler.setQualifiedHandler(qualifiedHandler);
+ qualifiedHandler.setClassNameGroupHandler(classNameGroupHandler);
}
- firstGroup = group;
- firstIndexFromCurrent = nextIndexOf;
+ lastHandler = handler;
+ handlers.add(handler);
}
+ lastCommittedIndex = i + 1;
+ seenPercentage = false;
+ } else {
+ seenPercentage = !escaped && regularExpression.charAt(i) == '%';
+ escaped = !escaped && regularExpression.charAt(i) == '\\';
}
- if (firstGroup != null) {
- String captureGroupName = CAPTURE_GROUP_PREFIX + (captureGroupIndex++);
- String patternToInsert = "(?<" + captureGroupName + ">" + firstGroup.subExpression() + ")";
- regularExpression =
- regularExpression.substring(0, firstIndexFromCurrent)
- + patternToInsert
- + regularExpression.substring(
- firstIndexFromCurrent + firstGroup.shortName().length());
- handlers.add(firstGroup.createHandler(captureGroupName));
- firstIndexFromCurrent += patternToInsert.length();
- }
- currentIndex = firstIndexFromCurrent;
}
- return regularExpression;
+ refinedRegularExpression.append(
+ regularExpression, lastCommittedIndex, regularExpression.length());
+ return captureGroupIndex;
}
- static class RetraceString {
+ private boolean isTypeOrBinarySeparator(String regularExpression, int startIndex, int endIndex) {
+ assert endIndex < regularExpression.length();
+ if (startIndex + 1 != endIndex) {
+ return false;
+ }
+ if (regularExpression.charAt(startIndex) != '\\') {
+ return false;
+ }
+ return regularExpression.charAt(startIndex + 1) == '.'
+ || regularExpression.charAt(startIndex + 1) == '/';
+ }
+ private RegularExpressionGroup getGroupFromVariable(char variable) {
+ switch (variable) {
+ case 'c':
+ return typeNameGroup;
+ case 'C':
+ return binaryNameGroup;
+ case 'm':
+ return methodNameGroup;
+ case 'f':
+ return fieldNameGroup;
+ case 's':
+ return sourceFileGroup;
+ case 'l':
+ return lineNumberGroup;
+ case 'S':
+ return sourceFileLineNumberGroup;
+ case 't':
+ return fieldOrReturnTypeGroup;
+ case 'a':
+ return methodArgumentsGroup;
+ default:
+ throw new Unreachable("Unexpected variable: " + variable);
+ }
+ }
+
+ static class RetraceStringContext {
private final Element classContext;
- private final ClassNameGroup classNameGroup;
private final ClassReference qualifiedContext;
+ private final String methodName;
private final RetraceMethodResult.Element methodContext;
- private final TypeReference typeOrReturnTypeContext;
- private final boolean hasTypeOrReturnTypeContext;
- private final String retracedString;
- private final int adjustedIndex;
- private final boolean isAmbiguous;
- private final int lineNumber;
+ private final int minifiedLineNumber;
+ private final int originalLineNumber;
private final String source;
+ private final boolean isAmbiguous;
- private RetraceString(
+ private RetraceStringContext(
Element classContext,
- ClassNameGroup classNameGroup,
ClassReference qualifiedContext,
+ String methodName,
RetraceMethodResult.Element methodContext,
- TypeReference typeOrReturnTypeContext,
- boolean hasTypeOrReturnTypeContext,
- String retracedString,
- int adjustedIndex,
- boolean isAmbiguous,
- int lineNumber,
- String source) {
+ int minifiedLineNumber,
+ int originalLineNumber,
+ String source,
+ boolean isAmbiguous) {
this.classContext = classContext;
- this.classNameGroup = classNameGroup;
this.qualifiedContext = qualifiedContext;
+ this.methodName = methodName;
this.methodContext = methodContext;
- this.typeOrReturnTypeContext = typeOrReturnTypeContext;
- this.hasTypeOrReturnTypeContext = hasTypeOrReturnTypeContext;
- this.retracedString = retracedString;
- this.adjustedIndex = adjustedIndex;
- this.isAmbiguous = isAmbiguous;
- this.lineNumber = lineNumber;
+ this.minifiedLineNumber = minifiedLineNumber;
+ this.originalLineNumber = originalLineNumber;
this.source = source;
+ this.isAmbiguous = isAmbiguous;
}
- String getRetracedString() {
- return retracedString;
+ private static RetraceStringContext empty() {
+ return new RetraceStringContext(null, null, null, null, NO_MATCH, NO_MATCH, null, false);
}
- boolean hasTypeOrReturnTypeContext() {
- return hasTypeOrReturnTypeContext;
+ private RetraceStringContext withClassContext(
+ Element classContext, ClassReference qualifiedContext) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
}
- Element getClassContext() {
- return classContext;
+ private RetraceStringContext withMethodName(String methodName) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
}
- RetraceMethodResult.Element getMethodContext() {
- return methodContext;
+ private RetraceStringContext withMethodContext(
+ RetraceMethodResult.Element methodContext,
+ ClassReference qualifiedContext,
+ boolean isAmbiguous) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
}
- TypeReference getTypeOrReturnTypeContext() {
- return typeOrReturnTypeContext;
+ private RetraceStringContext withQualifiedContext(ClassReference qualifiedContext) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
}
- public ClassReference getQualifiedContext() {
- return qualifiedContext;
+ public RetraceStringContext withSource(String source) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
}
- RetraceStringBuilder transform() {
- return RetraceStringBuilder.create(this);
+ public RetraceStringContext withLineNumbers(int minifiedLineNumber, int originalLineNumber) {
+ return new RetraceStringContext(
+ classContext,
+ qualifiedContext,
+ methodName,
+ methodContext,
+ minifiedLineNumber,
+ originalLineNumber,
+ source,
+ isAmbiguous);
+ }
+ }
+
+ static class RetraceStringBuilder {
+
+ private final StringBuilder retracedString;
+ private int lastCommittedIndex;
+
+ private RetraceStringBuilder(String retracedString, int lastCommittedIndex) {
+ this.retracedString = new StringBuilder(retracedString);
+ this.lastCommittedIndex = lastCommittedIndex;
}
- public int getLineNumber() {
- return lineNumber;
+ private void appendRetracedString(
+ String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
+ retracedString.append(source, lastCommittedIndex, originalFromIndex);
+ retracedString.append(stringToAppend);
+ lastCommittedIndex = originalToIndex;
}
- public String getSource() {
- return source;
+ private String build(String source) {
+ return retracedString.append(source, lastCommittedIndex, source.length()).toString();
+ }
+ }
+
+ private static class RetraceString {
+
+ private final RetraceStringBuilder builder;
+ private final RetraceStringContext context;
+
+ private RetraceString(RetraceStringBuilder builder, RetraceStringContext context) {
+ this.builder = builder;
+ this.context = context;
}
- static class RetraceStringBuilder {
+ private Element getClassContext() {
+ return context.classContext;
+ }
- private Element classContext;
- private ClassNameGroup classNameGroup;
- private ClassReference qualifiedContext;
- private RetraceMethodResult.Element methodContext;
- private TypeReference typeOrReturnTypeContext;
- private boolean hasTypeOrReturnTypeContext;
- private String retracedString;
- private int adjustedIndex;
- private boolean isAmbiguous;
- private int lineNumber;
- private String source;
+ private RetraceMethodResult.Element getMethodContext() {
+ return context.methodContext;
+ }
- private int maxReplaceStringIndex = NO_MATCH;
+ private String getSource() {
+ return context.source;
+ }
- private RetraceStringBuilder(
- Element classContext,
- ClassNameGroup classNameGroup,
- ClassReference qualifiedContext,
- RetraceMethodResult.Element methodContext,
- TypeReference typeOrReturnTypeContext,
- boolean hasTypeOrReturnTypeContext,
- String retracedString,
- int adjustedIndex,
- boolean isAmbiguous,
- int lineNumber,
- String source) {
- this.classContext = classContext;
- this.classNameGroup = classNameGroup;
- this.qualifiedContext = qualifiedContext;
- this.methodContext = methodContext;
- this.typeOrReturnTypeContext = typeOrReturnTypeContext;
- this.hasTypeOrReturnTypeContext = hasTypeOrReturnTypeContext;
- this.retracedString = retracedString;
- this.adjustedIndex = adjustedIndex;
- this.isAmbiguous = isAmbiguous;
- this.lineNumber = lineNumber;
- this.source = source;
- }
+ private int getLineNumber() {
+ return context.originalLineNumber;
+ }
- static RetraceStringBuilder create(String string) {
- return new RetraceStringBuilder(
- null, null, null, null, null, false, string, 0, false, 0, "");
- }
+ private boolean isAmbiguous() {
+ return context.isAmbiguous;
+ }
- static RetraceStringBuilder create(RetraceString string) {
- return new RetraceStringBuilder(
- string.classContext,
- string.classNameGroup,
- string.qualifiedContext,
- string.methodContext,
- string.typeOrReturnTypeContext,
- string.hasTypeOrReturnTypeContext,
- string.retracedString,
- string.adjustedIndex,
- string.isAmbiguous,
- string.lineNumber,
- string.source);
- }
+ private static RetraceString start(RetraceStringContext initialContext) {
+ return new RetraceString(new RetraceStringBuilder("", 0), initialContext);
+ }
- RetraceStringBuilder setClassContext(Element classContext, ClassNameGroup classNameGroup) {
- this.classContext = classContext;
- this.classNameGroup = classNameGroup;
- return this;
- }
+ private RetraceString updateContext(
+ Function<RetraceStringContext, RetraceStringContext> update) {
+ return new RetraceString(builder, update.apply(context));
+ }
- RetraceStringBuilder setMethodContext(RetraceMethodResult.Element methodContext) {
- this.methodContext = methodContext;
- return this;
- }
+ private RetraceString duplicate(RetraceStringContext newContext) {
+ return new RetraceString(
+ new RetraceStringBuilder(builder.retracedString.toString(), builder.lastCommittedIndex),
+ newContext);
+ }
- RetraceStringBuilder setTypeOrReturnTypeContext(TypeReference typeOrReturnTypeContext) {
- hasTypeOrReturnTypeContext = true;
- this.typeOrReturnTypeContext = typeOrReturnTypeContext;
- return this;
- }
-
- RetraceStringBuilder setQualifiedContext(ClassReference qualifiedContext) {
- this.qualifiedContext = qualifiedContext;
- return this;
- }
-
- RetraceStringBuilder setAmbiguous(boolean isAmbiguous) {
- this.isAmbiguous = isAmbiguous;
- return this;
- }
-
- RetraceStringBuilder setLineNumber(int lineNumber) {
- this.lineNumber = lineNumber;
- return this;
- }
-
- RetraceStringBuilder setSource(String source) {
- this.source = source;
- return this;
- }
-
- RetraceStringBuilder replaceInString(String oldString, String newString) {
- int oldStringStartIndex = retracedString.indexOf(oldString);
- assert oldStringStartIndex > NO_MATCH;
- int oldStringEndIndex = oldStringStartIndex + oldString.length();
- return replaceInStringRaw(newString, oldStringStartIndex, oldStringEndIndex);
- }
-
- RetraceStringBuilder replaceInString(String newString, int originalFrom, int originalTo) {
- return replaceInStringRaw(
- newString, originalFrom + adjustedIndex, originalTo + adjustedIndex);
- }
-
- RetraceStringBuilder replaceInStringRaw(String newString, int from, int to) {
- assert from <= to;
- assert from > maxReplaceStringIndex;
- String prefix = retracedString.substring(0, from);
- String postFix = retracedString.substring(to);
- this.retracedString = prefix + newString + postFix;
- this.adjustedIndex = adjustedIndex + newString.length() - (to - from);
- maxReplaceStringIndex = prefix.length() + newString.length();
- return this;
- }
-
- RetraceString build() {
- return new RetraceString(
- classContext,
- classNameGroup,
- qualifiedContext,
- methodContext,
- typeOrReturnTypeContext,
- hasTypeOrReturnTypeContext,
- retracedString,
- adjustedIndex,
- isAmbiguous,
- lineNumber,
- source);
- }
+ private RetraceString appendRetracedString(
+ String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
+ builder.appendRetracedString(source, stringToAppend, originalFromIndex, originalToIndex);
+ return this;
}
}
private interface RegularExpressionGroupHandler {
List<RetraceString> handleMatch(
- List<RetraceString> strings, Matcher matcher, RetraceBase retraceBase);
+ String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer);
+
+ default RetraceStringContext buildInitial(
+ RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+ return context;
+ }
+
+ default boolean isClassNameGroupHandler() {
+ return false;
+ }
+
+ default ClassNameGroupHandler asClassNameGroupHandler() {
+ return null;
+ }
+
+ default boolean isQualifiedHandler() {
+ return false;
+ }
+
+ default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
+ return null;
+ }
+ }
+
+ private interface QualifiedRegularExpressionGroupHandler extends RegularExpressionGroupHandler {
+
+ @Override
+ default boolean isQualifiedHandler() {
+ return true;
+ }
+
+ @Override
+ default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
+ return this;
+ }
+
+ void setClassNameGroupHandler(ClassNameGroupHandler handler);
}
private abstract static class RegularExpressionGroup {
- abstract String shortName();
-
abstract String subExpression();
abstract RegularExpressionGroupHandler createHandler(String captureGroup);
+
+ boolean isSynthetic() {
+ return false;
+ }
}
// TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
- private static final String javaIdentifierSegment = "[\\p{L}\\p{N}_\\p{Sc}]+";
+ private static final String javaIdentifierSegment =
+ "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
- private abstract static class ClassNameGroup extends RegularExpressionGroup {
+ private static final String METHOD_NAME_REGULAR_EXPRESSION =
+ "(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
+
+ abstract static class ClassNameGroup extends RegularExpressionGroup {
abstract String getClassName(ClassReference classReference);
@@ -400,41 +491,107 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
+ return new ClassNameGroupHandler(this, captureGroup);
+ }
+
+ static class ClassNameGroupHandler implements RegularExpressionGroupHandler {
+
+ private RetraceClassResult retraceClassResult = null;
+ private final ClassNameGroup classNameGroup;
+ private final String captureGroup;
+ private RegularExpressionGroupHandler qualifiedHandler;
+
+ public ClassNameGroupHandler(ClassNameGroup classNameGroup, String captureGroup) {
+ this.classNameGroup = classNameGroup;
+ this.captureGroup = captureGroup;
+ }
+
+ @Override
+ public List<RetraceString> handleMatch(
+ String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
return strings;
}
String typeName = matcher.group(captureGroup);
- RetraceClassResult retraceResult = retraceBase.retrace(classFromMatch(typeName));
- List<RetraceString> retracedStrings = new ArrayList<>();
+ RetraceClassResult retraceResult =
+ retraceClassResult == null
+ ? retracer.retrace(classNameGroup.classFromMatch(typeName))
+ : retraceClassResult;
+ assert !retraceResult.isAmbiguous();
+ List<RetraceString> retraceStrings = new ArrayList<>(strings.size());
for (RetraceString retraceString : strings) {
retraceResult.forEach(
element -> {
- retracedStrings.add(
- retraceString
- .transform()
- .setClassContext(element, this)
- .setMethodContext(null)
- .replaceInString(
- getClassName(element.getClassReference()),
- matcher.start(captureGroup),
- matcher.end(captureGroup))
- .build());
+ final RetraceString newRetraceString =
+ retraceString.updateContext(
+ context -> context.withClassContext(element, element.getClassReference()));
+ retraceStrings.add(newRetraceString);
+ if (qualifiedHandler == null) {
+ // If there is no qualified handler, commit right away.
+ newRetraceString.builder.appendRetracedString(
+ original,
+ classNameGroup.getClassName(element.getClassReference()),
+ startOfGroup,
+ matcher.end(captureGroup));
+ }
});
}
- return retracedStrings;
- };
+ return retraceStrings;
+ }
+
+ void commitClassName(
+ String original,
+ RetraceString retraceString,
+ ClassReference qualifiedContext,
+ Matcher matcher) {
+ if (matcher.start(captureGroup) == NO_MATCH) {
+ return;
+ }
+ retraceString.builder.appendRetracedString(
+ original,
+ classNameGroup.getClassName(qualifiedContext),
+ matcher.start(captureGroup),
+ matcher.end(captureGroup));
+ }
+
+ @Override
+ public RetraceStringContext buildInitial(
+ RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+ // Reset the local class context since this the same handler is used for multiple lines.
+ retraceClassResult = null;
+ if (matcher.start(captureGroup) == NO_MATCH || context.classContext != null) {
+ return context;
+ }
+ String typeName = matcher.group(captureGroup);
+ retraceClassResult = retracer.retrace(classNameGroup.classFromMatch(typeName));
+ assert !retraceClassResult.isAmbiguous();
+ Box<RetraceStringContext> box = new Box<>();
+ retraceClassResult.forEach(
+ element -> box.set(context.withClassContext(element, element.getClassReference())));
+ return box.get();
+ }
+
+ public void setQualifiedHandler(RegularExpressionGroupHandler handler) {
+ assert handler.isQualifiedHandler();
+ this.qualifiedHandler = handler;
+ }
+
+ @Override
+ public boolean isClassNameGroupHandler() {
+ return true;
+ }
+
+ @Override
+ public ClassNameGroupHandler asClassNameGroupHandler() {
+ return this;
+ }
}
}
private static class TypeNameGroup extends ClassNameGroup {
@Override
- String shortName() {
- return "%c";
- }
-
- @Override
String subExpression() {
return "(" + javaIdentifierSegment + "\\.)*" + javaIdentifierSegment;
}
@@ -453,11 +610,6 @@
private static class BinaryNameGroup extends ClassNameGroup {
@Override
- String shortName() {
- return "%C";
- }
-
- @Override
String subExpression() {
return "(?:" + javaIdentifierSegment + "\\/)*" + javaIdentifierSegment;
}
@@ -475,78 +627,112 @@
private static class MethodNameGroup extends RegularExpressionGroup {
- @Override
- String shortName() {
- return "%m";
+ private final boolean printVerbose;
+
+ public MethodNameGroup(boolean printVerbose) {
+ this.printVerbose = printVerbose;
}
@Override
String subExpression() {
- return "(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
+ return METHOD_NAME_REGULAR_EXPRESSION;
}
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
- return strings;
+ return new QualifiedRegularExpressionGroupHandler() {
+
+ private ClassNameGroupHandler classNameGroupHandler;
+
+ @Override
+ public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
+ classNameGroupHandler = handler;
}
- String methodName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- if (retraceString.classContext == null) {
- retracedStrings.add(retraceString);
- continue;
+
+ @Override
+ public List<RetraceString> handleMatch(
+ String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ if (classNameGroupHandler != null) {
+ for (RetraceString string : strings) {
+ classNameGroupHandler.commitClassName(
+ original, string, string.context.qualifiedContext, matcher);
+ }
+ }
+ return strings;
}
- retraceString
- .getClassContext()
- .lookupMethod(methodName)
- .forEach(
- element -> {
- MethodReference methodReference = element.getMethodReference();
- if (retraceString.hasTypeOrReturnTypeContext()) {
- if (methodReference.getReturnType() == null
- && retraceString.getTypeOrReturnTypeContext() != null) {
- return;
- } else if (methodReference.getReturnType() != null
- && !methodReference
- .getReturnType()
- .equals(retraceString.getTypeOrReturnTypeContext())) {
- return;
- }
- }
- RetraceStringBuilder newRetraceString = retraceString.transform();
- ClassReference existingClass =
- retraceString.getClassContext().getClassReference();
- ClassReference holder = methodReference.getHolderClass();
- if (holder != existingClass) {
- // The element is defined on another holder.
- newRetraceString
- .replaceInString(
- newRetraceString.classNameGroup.getClassName(existingClass),
- newRetraceString.classNameGroup.getClassName(holder))
- .setQualifiedContext(holder);
- }
- newRetraceString
- .setMethodContext(element)
- .setAmbiguous(element.getRetraceMethodResult().isAmbiguous())
- .replaceInString(
- methodReference.getMethodName(),
- matcher.start(captureGroup),
- matcher.end(captureGroup));
- retracedStrings.add(newRetraceString.build());
- });
+ String methodName = matcher.group(captureGroup);
+ List<RetraceString> retracedStrings = new ArrayList<>();
+ for (RetraceString retraceString : strings) {
+ retraceMethodForString(
+ retraceString,
+ methodName,
+ (element, newContext) -> {
+ final RetraceString newRetraceString = retraceString.duplicate(newContext);
+ if (classNameGroupHandler != null) {
+ classNameGroupHandler.commitClassName(
+ original,
+ newRetraceString,
+ element.getMethodReference().getHolderClass(),
+ matcher);
+ }
+ retracedStrings.add(
+ newRetraceString.appendRetracedString(
+ original,
+ printVerbose
+ ? RetraceUtils.methodDescriptionFromMethodReference(
+ element.getMethodReference(), false, true)
+ : element.getMethodReference().getMethodName(),
+ startOfGroup,
+ matcher.end(captureGroup)));
+ });
+ }
+ return retracedStrings;
}
- return retracedStrings;
+
+ @Override
+ public RetraceStringContext buildInitial(
+ RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH || context.methodName != null) {
+ return context;
+ }
+ return context.withMethodName(matcher.group(captureGroup));
+ }
};
}
+
+ private static void retraceMethodForString(
+ RetraceString retraceString,
+ String methodName,
+ BiConsumer<RetraceMethodResult.Element, RetraceStringContext> process) {
+ if (retraceString.context.classContext == null) {
+ return;
+ }
+ RetraceMethodResult retraceMethodResult =
+ retraceString.getClassContext().lookupMethod(methodName);
+ if (retraceString.context.minifiedLineNumber > NO_MATCH) {
+ retraceMethodResult =
+ retraceMethodResult.narrowByLine(retraceString.context.minifiedLineNumber);
+ }
+ retraceMethodResult.forEach(
+ element ->
+ process.accept(
+ element,
+ retraceString.context.withMethodContext(
+ element,
+ element.getMethodReference().getHolderClass(),
+ element.getRetraceMethodResult().isAmbiguous())));
+ }
}
private static class FieldNameGroup extends RegularExpressionGroup {
- @Override
- String shortName() {
- return "%f";
+ private final boolean printVerbose;
+
+ public FieldNameGroup(boolean printVerbose) {
+ this.printVerbose = printVerbose;
}
@Override
@@ -556,86 +742,112 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
- return strings;
+ return new QualifiedRegularExpressionGroupHandler() {
+
+ private ClassNameGroupHandler classNameGroupHandler;
+
+ @Override
+ public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
+ classNameGroupHandler = handler;
}
- String methodName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- if (retraceString.getClassContext() == null) {
- retracedStrings.add(retraceString);
- continue;
+
+ @Override
+ public List<RetraceString> handleMatch(
+ String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ if (classNameGroupHandler != null) {
+ for (RetraceString string : strings) {
+ classNameGroupHandler.commitClassName(
+ original, string, string.context.qualifiedContext, matcher);
+ }
+ }
+ return strings;
}
- retraceString
- .getClassContext()
- .lookupField(methodName)
- .forEach(
- element -> {
- RetraceStringBuilder newRetraceString = retraceString.transform();
- ClassReference existingClass =
- retraceString.getClassContext().getClassReference();
- ClassReference holder = element.getFieldReference().getHolderClass();
- if (holder != existingClass) {
- // The element is defined on another holder.
- newRetraceString
- .replaceInString(
- newRetraceString.classNameGroup.getClassName(existingClass),
- newRetraceString.classNameGroup.getClassName(holder))
- .setQualifiedContext(holder);
- }
- newRetraceString.replaceInString(
- element.getFieldReference().getFieldName(),
- matcher.start(captureGroup),
- matcher.end(captureGroup));
- retracedStrings.add(newRetraceString.build());
- });
+ String methodName = matcher.group(captureGroup);
+ List<RetraceString> retracedStrings = new ArrayList<>();
+ for (RetraceString retraceString : strings) {
+ if (retraceString.getClassContext() == null) {
+ assert classNameGroupHandler == null;
+ return strings;
+ }
+ final RetraceFieldResult retraceFieldResult =
+ retraceString.getClassContext().lookupField(methodName);
+ assert !retraceFieldResult.isAmbiguous();
+ retraceFieldResult.forEach(
+ element -> {
+ if (classNameGroupHandler != null) {
+ classNameGroupHandler.commitClassName(
+ original,
+ retraceString,
+ element.getFieldReference().getHolderClass(),
+ matcher);
+ }
+ retracedStrings.add(
+ retraceString
+ .updateContext(
+ context ->
+ context.withQualifiedContext(
+ element.getFieldReference().getHolderClass()))
+ .appendRetracedString(
+ original,
+ getFieldString(element.getFieldReference()),
+ startOfGroup,
+ matcher.end(captureGroup)));
+ });
+ }
+ return retracedStrings;
}
- return retracedStrings;
};
}
+
+ private String getFieldString(FieldReference fieldReference) {
+ if (!printVerbose) {
+ return fieldReference.getFieldName();
+ }
+ return fieldReference.getFieldType().getTypeName() + " " + fieldReference.getFieldName();
+ }
}
private static class SourceFileGroup extends RegularExpressionGroup {
@Override
- String shortName() {
- return "%s";
- }
-
- @Override
String subExpression() {
return "(?:(\\w*[\\. ])?(\\w*)?)";
}
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
+ return (original, strings, matcher, retracer) -> {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
return strings;
}
String fileName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = new ArrayList<>();
+ List<RetraceString> retracedStrings = null;
for (RetraceString retraceString : strings) {
- if (retraceString.classContext == null) {
- retracedStrings.add(retraceString);
- continue;
+ if (retraceString.context.classContext == null) {
+ return strings;
}
- String newSourceFile =
- retraceString.getQualifiedContext() != null
- ? retraceBase.retraceSourceFile(
- retraceString.classContext.getClassReference(),
+ if (retracedStrings == null) {
+ retracedStrings = new ArrayList<>();
+ }
+ RetraceSourceFileResult sourceFileResult =
+ retraceString.getMethodContext() != null
+ ? retraceString.getMethodContext().retraceSourceFile(fileName)
+ : RetraceUtils.getSourceFile(
+ retraceString.getClassContext(),
+ retraceString.context.qualifiedContext,
fileName,
- retraceString.getQualifiedContext(),
- true)
- : retraceString.classContext.retraceSourceFile(fileName, retraceBase);
+ retracer);
retracedStrings.add(
retraceString
- .transform()
- .setSource(fileName)
- .replaceInString(
- newSourceFile, matcher.start(captureGroup), matcher.end(captureGroup))
- .build());
+ .updateContext(context -> context.withSource(sourceFileResult.getFilename()))
+ .appendRetracedString(
+ original,
+ sourceFileResult.getFilename(),
+ startOfGroup,
+ matcher.end(captureGroup)));
}
return retracedStrings;
};
@@ -645,97 +857,133 @@
private class LineNumberGroup extends RegularExpressionGroup {
@Override
- String shortName() {
- return "%l";
- }
-
- @Override
String subExpression() {
return "\\d*";
}
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
- return strings;
+ return new RegularExpressionGroupHandler() {
+ @Override
+ public List<RetraceString> handleMatch(
+ String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ return strings;
+ }
+ String lineNumberAsString = matcher.group(captureGroup);
+ if (lineNumberAsString.isEmpty()) {
+ return strings;
+ }
+ int lineNumber = Integer.parseInt(lineNumberAsString);
+ List<RetraceString> retracedStrings = new ArrayList<>();
+ for (RetraceString retraceString : strings) {
+ RetraceMethodResult.Element methodContext = retraceString.context.methodContext;
+ if (methodContext == null) {
+ if (retraceString.context.classContext == null
+ || retraceString.context.methodName == null) {
+ // We have no way of retracing the line number.
+ retracedStrings.add(retraceString);
+ continue;
+ }
+ // This situation arises when we have a matched pattern as %l..%c.%m where the
+ // line number handler is defined before the methodname handler.
+ MethodNameGroup.retraceMethodForString(
+ retraceString,
+ retraceString.context.methodName,
+ (element, newContext) -> {
+ // The same method can be represented multiple times if it has multiple
+ // mappings.
+ if (element.hasNoLineNumberRange()
+ || !element.containsMinifiedLineNumber(lineNumber)) {
+ diagnosticsHandler.info(
+ new StringDiagnostic(
+ "Pruning "
+ + retraceString.builder.retracedString.toString()
+ + " from result because method is not in range on line number "
+ + lineNumber));
+ }
+ final int originalLineNumber = element.getOriginalLineNumber(lineNumber);
+ retracedStrings.add(
+ retraceString
+ .updateContext(
+ context -> context.withLineNumbers(lineNumber, originalLineNumber))
+ .appendRetracedString(
+ original,
+ originalLineNumber + "",
+ startOfGroup,
+ matcher.end(captureGroup)));
+ });
+ continue;
+ }
+ // If the method context is unknown, do nothing.
+ if (methodContext.getMethodReference().isUnknown()
+ || methodContext.hasNoLineNumberRange()) {
+ retracedStrings.add(retraceString);
+ continue;
+ }
+ int originalLineNumber = methodContext.getOriginalLineNumber(lineNumber);
+ retracedStrings.add(
+ retraceString
+ .updateContext(
+ context -> context.withLineNumbers(lineNumber, originalLineNumber))
+ .appendRetracedString(
+ original,
+ originalLineNumber + "",
+ startOfGroup,
+ matcher.end(captureGroup)));
+ }
+ return retracedStrings;
}
- String lineNumberAsString = matcher.group(captureGroup);
- int lineNumber =
- lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString);
- List<RetraceString> retracedStrings = new ArrayList<>();
- boolean seenRange = false;
- for (RetraceString retraceString : strings) {
- RetraceMethodResult.Element methodContext = retraceString.methodContext;
- if (methodContext == null || methodContext.getMethodReference().isUnknown()) {
- retracedStrings.add(retraceString);
- continue;
+
+ @Override
+ public RetraceStringContext buildInitial(
+ RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+ if (matcher.start(captureGroup) == NO_MATCH || context.minifiedLineNumber > NO_MATCH) {
+ return context;
}
- if (methodContext.hasNoLineNumberRange()) {
- continue;
- }
- seenRange = true;
- Set<MethodReference> narrowedSet =
- methodContext.getRetraceMethodResult().narrowByLine(lineNumber).stream()
- .map(RetraceMethodResult.Element::getMethodReference)
- .collect(Collectors.toSet());
- if (!narrowedSet.contains(methodContext.getMethodReference())) {
- // Prune the retraceString since we now have line number information and this is not
- // a part of the result.
- diagnosticsHandler.info(
- new StringDiagnostic(
- "Pruning "
- + retraceString.getRetracedString()
- + " from result because method is not defined on line number "
- + lineNumber));
- continue;
- }
- // The same method can be represented multiple times if it has multiple mappings.
- if (!methodContext.containsMinifiedLineNumber(lineNumber)) {
- diagnosticsHandler.info(
- new StringDiagnostic(
- "Pruning "
- + retraceString.getRetracedString()
- + " from result because method is not in range on line number "
- + lineNumber));
- continue;
- }
- int originalLineNumber = methodContext.getOriginalLineNumber(lineNumber);
- retracedStrings.add(
- retraceString
- .transform()
- .setAmbiguous(false)
- .setLineNumber(originalLineNumber)
- .replaceInString(
- originalLineNumber + "",
- matcher.start(captureGroup),
- matcher.end(captureGroup))
- .build());
+ String lineNumberAsString = matcher.group(captureGroup);
+ return context.withLineNumbers(
+ lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString),
+ NO_MATCH);
}
- return seenRange ? retracedStrings : strings;
};
}
}
+ private static class SourceFileLineNumberGroup extends RegularExpressionGroup {
+
+ @Override
+ String subExpression() {
+ return "%s(?::%l)?";
+ }
+
+ @Override
+ RegularExpressionGroupHandler createHandler(String captureGroup) {
+ throw new Unreachable("Should never be called");
+ }
+
+ @Override
+ boolean isSynthetic() {
+ return true;
+ }
+ }
+
private static final String JAVA_TYPE_REGULAR_EXPRESSION =
"(" + javaIdentifierSegment + "\\.)*" + javaIdentifierSegment + "[\\[\\]]*";
private static class FieldOrReturnTypeGroup extends RegularExpressionGroup {
@Override
- String shortName() {
- return "%t";
- }
-
- @Override
String subExpression() {
return JAVA_TYPE_REGULAR_EXPRESSION;
}
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
+ return (original, strings, matcher, retracer) -> {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
return strings;
}
String typeName = matcher.group(captureGroup);
@@ -744,34 +992,25 @@
return strings;
}
TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
- List<RetraceString> retracedStrings = new ArrayList<>();
- RetraceTypeResult retracedType = retraceBase.retrace(typeReference);
+ RetraceTypeResult retracedType = retracer.retrace(typeReference);
+ assert !retracedType.isAmbiguous();
for (RetraceString retraceString : strings) {
retracedType.forEach(
element -> {
TypeReference retracedReference = element.getTypeReference();
- retracedStrings.add(
- retraceString
- .transform()
- .setTypeOrReturnTypeContext(retracedReference)
- .replaceInString(
- retracedReference == null ? "void" : retracedReference.getTypeName(),
- matcher.start(captureGroup),
- matcher.end(captureGroup))
- .build());
+ retraceString.appendRetracedString(
+ original,
+ retracedReference == null ? "void" : retracedReference.getTypeName(),
+ startOfGroup,
+ matcher.end(captureGroup));
});
}
- return retracedStrings;
+ return strings;
};
}
}
- private class MethodArgumentsGroup extends RegularExpressionGroup {
-
- @Override
- String shortName() {
- return "%a";
- }
+ private static class MethodArgumentsGroup extends RegularExpressionGroup {
@Override
String subExpression() {
@@ -780,74 +1019,36 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (strings, matcher, retraceBase) -> {
- if (matcher.start(captureGroup) == NO_MATCH) {
+ return (original, strings, matcher, retracer) -> {
+ final int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
return strings;
}
- Set<List<TypeReference>> initialValue = new LinkedHashSet<>();
- initialValue.add(new ArrayList<>());
- Set<List<TypeReference>> allRetracedReferences =
+ final String formals =
Arrays.stream(matcher.group(captureGroup).split(","))
- .map(String::trim)
- .reduce(
- initialValue,
- (acc, typeName) -> {
+ .map(
+ typeName -> {
+ typeName = typeName.trim();
+ if (typeName.isEmpty()) {
+ return null;
+ }
String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
- return acc;
+ return typeName;
}
- TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
- Set<List<TypeReference>> retracedTypes = new LinkedHashSet<>();
- retraceBase
- .retrace(typeReference)
- .forEach(
- element -> {
- for (List<TypeReference> currentReferences : acc) {
- ArrayList<TypeReference> newList =
- new ArrayList<>(currentReferences);
- newList.add(element.getTypeReference());
- retracedTypes.add(newList);
- }
- });
- return retracedTypes;
- },
- (l1, l2) -> {
- l1.addAll(l2);
- return l1;
- });
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- if (retraceString.getMethodContext() != null
- && !allRetracedReferences.contains(
- retraceString.getMethodContext().getMethodReference().getFormalTypes())) {
- // Prune the string since we now know the formals.
- String formals =
- retraceString.getMethodContext().getMethodReference().getFormalTypes().stream()
- .map(TypeReference::getTypeName)
- .collect(Collectors.joining(","));
- diagnosticsHandler.info(
- new StringDiagnostic(
- "Pruning "
- + retraceString.getRetracedString()
- + " from result because formals ("
- + formals
- + ") do not match result set."));
- continue;
- }
- for (List<TypeReference> retracedReferences : allRetracedReferences) {
- retracedStrings.add(
- retraceString
- .transform()
- .replaceInString(
- retracedReferences.stream()
- .map(TypeReference::getTypeName)
- .collect(Collectors.joining(",")),
- matcher.start(captureGroup),
- matcher.end(captureGroup))
- .build());
- }
+ final RetraceTypeResult retraceResult =
+ retracer.retrace(Reference.returnTypeFromDescriptor(descriptor));
+ assert !retraceResult.isAmbiguous();
+ final Box<TypeReference> elementBox = new Box<>();
+ retraceResult.forEach(element -> elementBox.set(element.getTypeReference()));
+ return elementBox.get().getTypeName();
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining(","));
+ for (RetraceString string : strings) {
+ string.appendRetracedString(original, formals, startOfGroup, matcher.end(captureGroup));
}
- return retracedStrings;
+ return strings;
};
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
new file mode 100644
index 0000000..4b21be1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceSourceFileResult.java
@@ -0,0 +1,27 @@
+// 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;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public class RetraceSourceFileResult {
+
+ private final String filename;
+ private final boolean synthesized;
+
+ RetraceSourceFileResult(String filename, boolean synthesized) {
+ this.filename = filename;
+ this.synthesized = synthesized;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public boolean isSynthesized() {
+ return synthesized;
+ }
+}
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 c63a707..fd28127 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -86,17 +86,17 @@
}
}
- private final RetraceBase retraceBase;
+ private final RetraceApi retracer;
private final List<String> stackTrace;
private final DiagnosticsHandler diagnosticsHandler;
private final boolean verbose;
RetraceStackTrace(
- RetraceBase retraceBase,
+ RetraceApi retracer,
List<String> stackTrace,
DiagnosticsHandler diagnosticsHandler,
boolean verbose) {
- this.retraceBase = retraceBase;
+ this.retracer = retracer;
this.stackTrace = stackTrace;
this.diagnosticsHandler = diagnosticsHandler;
this.verbose = verbose;
@@ -117,7 +117,7 @@
return;
}
StackTraceLine stackTraceLine = parseLine(index + 1, stackTrace.get(index));
- List<StackTraceLine> retraced = stackTraceLine.retrace(retraceBase, verbose);
+ List<StackTraceLine> retraced = stackTraceLine.retrace(retracer, verbose);
StackTraceNode node = new StackTraceNode(retraced);
result.add(node);
retraceLine(stackTrace, index + 1, result);
@@ -125,7 +125,7 @@
abstract static class StackTraceLine {
- abstract List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose);
+ abstract List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose);
static int firstNonWhiteSpaceCharacterFromIndex(String line, int index) {
return firstFromIndex(line, index, not(Character::isWhitespace));
@@ -225,9 +225,9 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
List<StackTraceLine> exceptionLines = new ArrayList<>();
- retraceBase
+ retracer
.retrace(Reference.classFromTypeName(exceptionClass))
.forEach(
element ->
@@ -397,31 +397,29 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
List<StackTraceLine> lines = new ArrayList<>();
String retraceClassLoaderName = classLoaderName;
if (retraceClassLoaderName != null) {
ClassReference classLoaderReference = Reference.classFromTypeName(retraceClassLoaderName);
- retraceBase
+ retracer
.retrace(classLoaderReference)
.forEach(
classElement -> {
retraceClassAndMethods(
- retraceBase, verbose, lines, classElement.getClassReference().getTypeName());
+ retracer, verbose, lines, classElement.getClassReference().getTypeName());
});
} else {
- retraceClassAndMethods(retraceBase, verbose, lines, retraceClassLoaderName);
+ retraceClassAndMethods(retracer, verbose, lines, retraceClassLoaderName);
}
return lines;
}
private void retraceClassAndMethods(
- RetraceBase retraceBase,
- boolean verbose,
- List<StackTraceLine> lines,
- String classLoaderName) {
+ RetraceApi retracer, boolean verbose, List<StackTraceLine> lines, String classLoaderName) {
ClassReference classReference = Reference.classFromTypeName(clazz);
- RetraceMethodResult retraceResult = retraceBase.retrace(classReference).lookupMethod(method);
+ RetraceClassResult classResult = retracer.retrace(classReference);
+ RetraceMethodResult retraceResult = classResult.lookupMethod(method);
if (linePosition != NO_POSITION && linePosition != INVALID_POSITION) {
retraceResult = retraceResult.narrowByLine(linePosition);
}
@@ -436,9 +434,8 @@
moduleName,
methodReference.getHolderClass().getTypeName(),
methodReference.getMethodName(),
- methodDescriptionFromMethodReference(methodReference, verbose),
- retraceBase.retraceSourceFile(
- classReference, fileName, methodReference.getHolderClass(), true),
+ methodDescriptionFromMethodReference(methodReference, true, verbose),
+ methodElement.retraceSourceFile(fileName).getFilename(),
hasLinePosition()
? methodElement.getOriginalLineNumber(linePosition)
: linePosition,
@@ -509,7 +506,7 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
return ImmutableList.of(new MoreLine(line));
}
@@ -562,9 +559,9 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
List<StackTraceLine> exceptionLines = new ArrayList<>();
- retraceBase
+ retracer
.retrace(Reference.classFromTypeName(exceptionClass))
.forEach(
element ->
@@ -590,7 +587,7 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
+ List<StackTraceLine> retrace(RetraceApi retracer, boolean verbose) {
return ImmutableList.of(new UnknownLine(line));
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
index 2ab72a7..b70602d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -13,11 +13,11 @@
public class RetraceTypeResult extends Result<Element, RetraceTypeResult> {
private final TypeReference obfuscatedType;
- private final RetraceBase retraceBase;
+ private final RetraceApi retracer;
- RetraceTypeResult(TypeReference obfuscatedType, RetraceBase retraceBase) {
+ RetraceTypeResult(TypeReference obfuscatedType, RetraceApi retracer) {
this.obfuscatedType = obfuscatedType;
- this.retraceBase = retraceBase;
+ this.retracer = retracer;
}
@Override
@@ -28,13 +28,17 @@
}
if (obfuscatedType.isArray()) {
int dimensions = obfuscatedType.asArray().getDimensions();
- return retraceBase.retrace(obfuscatedType.asArray().getBaseType()).stream()
+ return retracer.retrace(obfuscatedType.asArray().getBaseType()).stream()
.map(base -> new Element(Reference.array(base.getTypeReference(), dimensions)));
}
- return retraceBase.retrace(obfuscatedType.asClass()).stream()
+ return retracer.retrace(obfuscatedType.asClass()).stream()
.map(clazz -> new Element(clazz.getClassReference()));
}
+ public boolean isAmbiguous() {
+ return false;
+ }
+
@Override
public RetraceTypeResult forEach(Consumer<Element> resultConsumer) {
stream().forEach(resultConsumer);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
index 81c2272..77aff7d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -4,24 +4,34 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.Box;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import java.util.Set;
public class RetraceUtils {
+ private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
+ Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source", "PG");
+
public static String methodDescriptionFromMethodReference(
- MethodReference methodReference, boolean verbose) {
+ MethodReference methodReference, boolean appendHolder, boolean verbose) {
if (!verbose || methodReference.isUnknown()) {
return methodReference.getHolderClass().getTypeName() + "." + methodReference.getMethodName();
}
StringBuilder sb = new StringBuilder();
+ if (appendHolder) {
+ sb.append(methodReference.getHolderClass().getTypeName());
+ sb.append(".");
+ }
sb.append(
methodReference.getReturnType() == null
? "void"
: methodReference.getReturnType().getTypeName());
sb.append(" ");
- sb.append(methodReference.getHolderClass().getTypeName());
- sb.append(".");
sb.append(methodReference.getMethodName());
sb.append("(");
boolean seenFirstIndex = false;
@@ -35,4 +45,76 @@
sb.append(")");
return sb.toString();
}
+
+ public static boolean hasPredictableSourceFileName(String originalClassName, String sourceFile) {
+ String synthesizedSourceFileName = getClassSimpleName(originalClassName) + ".java";
+ return synthesizedSourceFileName.equals(sourceFile);
+ }
+
+ private static String getClassSimpleName(String clazz) {
+ int lastIndexOfPeriod = clazz.lastIndexOf('.');
+ // Check if we can find a subclass separator.
+ int endIndex = clazz.lastIndexOf('$');
+ if (lastIndexOfPeriod > endIndex || endIndex < 0) {
+ endIndex = clazz.length();
+ }
+ return clazz.substring(lastIndexOfPeriod + 1, endIndex);
+ }
+
+ static RetraceSourceFileResult getSourceFile(
+ RetraceClassResult.Element classElement,
+ ClassReference context,
+ String sourceFile,
+ RetraceApi retracer) {
+ // If no context is specified always retrace using the found class element.
+ if (context == null) {
+ return classElement.retraceSourceFile(sourceFile);
+ }
+ if (context.equals(classElement.getClassReference())) {
+ return classElement.retraceSourceFile(sourceFile);
+ } else {
+ RetraceClassResult contextClassResult = retracer.retrace(context);
+ assert !contextClassResult.isAmbiguous();
+ if (contextClassResult.hasRetraceResult()) {
+ Box<RetraceSourceFileResult> retraceSourceFile = new Box<>();
+ contextClassResult.forEach(
+ element -> retraceSourceFile.set(element.retraceSourceFile(sourceFile)));
+ return retraceSourceFile.get();
+ } else {
+ return new RetraceSourceFileResult(
+ synthesizeFileName(
+ context.getTypeName(),
+ classElement.getClassReference().getTypeName(),
+ sourceFile,
+ true),
+ true);
+ }
+ }
+ }
+
+ public static String synthesizeFileName(
+ String retracedClassName,
+ String minifiedClassName,
+ String sourceFile,
+ boolean hasRetraceResult) {
+ boolean fileNameProbablyChanged =
+ hasRetraceResult && !retracedClassName.startsWith(minifiedClassName);
+ if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
+ // We have no new information, only rewrite filename if it is unknown.
+ // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
+ return sourceFile;
+ }
+ String extension = Files.getFileExtension(sourceFile);
+ if (extension.isEmpty()) {
+ extension = "java";
+ }
+ if (!hasRetraceResult) {
+ // We have no mapping but but file name is unknown, so the best we can do is take the
+ // name of the obfuscated clazz.
+ assert minifiedClassName.equals(retracedClassName);
+ return getClassSimpleName(minifiedClassName) + "." + extension;
+ }
+ String newFileName = getClassSimpleName(retracedClassName);
+ return newFileName + "." + extension;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
new file mode 100644
index 0000000..0f7d547
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -0,0 +1,48 @@
+// 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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
+
+/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
+@Keep
+public class Retracer implements RetraceApi {
+
+ private final ClassNameMapper classNameMapper;
+
+ private Retracer(ClassNameMapper classNameMapper) {
+ this.classNameMapper = classNameMapper;
+ }
+
+ public static RetraceApi create(ClassNameMapper classNameMapper) {
+ return new Retracer(classNameMapper);
+ }
+
+ @Override
+ public RetraceMethodResult retrace(MethodReference methodReference) {
+ return retrace(methodReference.getHolderClass()).lookupMethod(methodReference.getMethodName());
+ }
+
+ @Override
+ public RetraceFieldResult retrace(FieldReference fieldReference) {
+ return retrace(fieldReference.getHolderClass()).lookupField(fieldReference.getFieldName());
+ }
+
+ @Override
+ public RetraceClassResult retrace(ClassReference classReference) {
+ return RetraceClassResult.create(
+ classReference, classNameMapper.getClassNaming(classReference.getTypeName()), this);
+ }
+
+ @Override
+ public RetraceTypeResult retrace(TypeReference typeReference) {
+ return new RetraceTypeResult(typeReference, this);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
new file mode 100644
index 0000000..bbe2d5b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
@@ -0,0 +1,64 @@
+// 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;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.DiagnosticsMatcher;
+import com.android.tools.r8.PositionMatcher;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+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 DuplicateMappingsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DuplicateMappingsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testSourceFileName() {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ Retrace.run(
+ RetraceCommand.builder(diagnosticsHandler)
+ .setProguardMapProducer(
+ () ->
+ StringUtils.lines(
+ "com.android.tools.r8.retrace.SourceFileTest$ClassWithCustomFileName ->"
+ + " com.android.tools.r8.retrace.a:",
+ "# {'id':'sourceFile','fileName':'foobarbaz.java'}",
+ "# {'id':'sourceFile','fileName':'foobarbaz2.java'}"))
+ .setStackTrace(ImmutableList.of())
+ .setRetracedStackTraceConsumer(
+ strings -> {
+ // No need to do anything, we are just checking for diagnostics.
+ })
+ .build());
+ diagnosticsHandler
+ .assertWarningsCount(1)
+ .assertWarningsMatch(
+ allOf(
+ DiagnosticsMatcher.diagnosticMessage(containsString("The mapping")),
+ DiagnosticsMatcher.diagnosticMessage(
+ containsString("is not allowed in combination with")),
+ DiagnosticsMatcher.diagnosticPosition(PositionMatcher.positionLine(3))));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 102f7f0..8a0f5e5 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -4,23 +4,27 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.retrace.RetraceTests.DEFAULT_REGULAR_EXPRESSION;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.Version;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTraceWithInfo;
import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
+import com.android.tools.r8.retrace.stacktraces.PGStackTrace;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.base.Charsets;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -38,6 +42,8 @@
@Rule public TemporaryFolder folder = new TemporaryFolder();
+ private static String SMILEY_EMOJI = "\uD83D\uDE00";
+
@Test
public void testPrintIdentityStackTraceFile() throws IOException {
runTest("", nonMappableStackTrace, false, nonMappableStackTrace);
@@ -70,14 +76,34 @@
}
@Test
+ public void testVerboseSingleHyphen() throws IOException {
+ FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
+ runTest(
+ stackTrace.mapping(),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
+ "-verbose");
+ }
+
+ @Test
public void testRegularExpression() throws IOException {
ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
runTest(
stackTrace.mapping(),
StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
false,
- StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
- "--regex=" + DEFAULT_REGULAR_EXPRESSION);
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
+ }
+
+ @Test
+ public void testRegularExpressionSingleHyphen() throws IOException {
+ ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
+ runTest(
+ stackTrace.mapping(),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
}
@Test
@@ -88,11 +114,20 @@
StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
false,
StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
- "--regex=" + DEFAULT_REGULAR_EXPRESSION,
"--info");
}
@Test
+ public void testPGStackTrace() throws Exception {
+ PGStackTrace pgStackTrace = new PGStackTrace();
+ runTest(
+ pgStackTrace.mapping(),
+ StringUtils.joinLines(pgStackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(pgStackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
+ }
+
+ @Test
public void testEmpty() throws IOException {
runTest("", "", false, "");
}
@@ -104,6 +139,23 @@
assertEquals(Retrace.USAGE_MESSAGE, processResult.stdout);
}
+ @Test
+ public void testVersion() throws Exception {
+ ProcessResult processResult = runRetraceCommandLine(null, Arrays.asList("--version"));
+ assertEquals(0, processResult.exitCode);
+ assertEquals(StringUtils.lines("Retrace " + Version.getVersionString()), processResult.stdout);
+ }
+
+ @Test
+ public void testNonAscii() throws IOException {
+ runTest("", SMILEY_EMOJI, false, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
+ }
+
+ @Test
+ public void testNonAsciiStdIn() throws IOException {
+ runTest("", SMILEY_EMOJI, true, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
+ }
+
private final String nonMappableStackTrace =
StringUtils.lines(
"com.android.r8.R8Exception: Problem when compiling program",
@@ -123,6 +175,14 @@
assertEquals(expected, result.stdout);
}
+ private void runTestNotEquals(
+ String mapping, String stackTrace, boolean stacktraceStdIn, String expected, String... args)
+ throws IOException {
+ ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn, args);
+ assertEquals(0, result.exitCode);
+ assertNotEquals(expected, result.stdout);
+ }
+
private void runAbortTest(Matcher<String> errorMatch, String... args) throws IOException {
ProcessResult result = runRetraceCommandLine(null, Arrays.asList(args));
assertEquals(1, result.exitCode);
@@ -135,7 +195,7 @@
Path mappingFile = folder.newFile("mapping.txt").toPath();
Files.write(mappingFile, mapping.getBytes());
File stackTraceFile = folder.newFile("stacktrace.txt");
- Files.write(stackTraceFile.toPath(), stackTrace.getBytes());
+ Files.write(stackTraceFile.toPath(), stackTrace.getBytes(StandardCharsets.UTF_8));
Collection<String> args = new ArrayList<>();
args.add(mappingFile.toString());
@@ -187,8 +247,8 @@
System.setErr(originalErr);
return new ProcessResult(
exitCode,
- outputByteStream.toString(),
- errorByteStream.toString(),
+ outputByteStream.toString(Charsets.UTF_8.name()),
+ errorByteStream.toString(Charsets.UTF_8.name()),
StringUtils.joinLines(args));
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index bb69fc3..4fb8389 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
import com.android.tools.r8.TestBase;
@@ -11,11 +12,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
+import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -24,9 +27,6 @@
@RunWith(Parameterized.class)
public class RetraceRegularExpressionTests extends TestBase {
- private static final String DEFAULT_REGULAR_EXPRESSION =
- "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)|(?:(?:.*?[:\"]\\s+)?%c(?::.*)?)";
-
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
@@ -395,7 +395,7 @@
}
@Test
- public void testNotFoundLineNumberInMethodContext() {
+ public void testNoLineNumberInMethodContext() {
runRetraceTest(
"%c\\.%m\\(%l\\)",
new StackTraceForTest() {
@@ -412,7 +412,35 @@
@Override
public List<String> retracedStackTrace() {
- return ImmutableList.of("a.b.c.a()");
+ return ImmutableList.of("com.android.tools.r8.R8.foo()");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+ });
+ }
+
+ @Test
+ public void testNotFoundLineNumberInMethodContext() {
+ runRetraceTest(
+ "%c\\.%m\\(%l\\)",
+ new StackTraceForTest() {
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of("a.b.c.a(42)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.R8 -> a.b.c:", " 3:3:boolean foo():7 -> a");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of("com.android.tools.r8.R8.a(42)");
}
@Override
@@ -540,6 +568,7 @@
}
@Test
+ @Ignore("b/165782924")
public void useReturnTypeToNarrowMethodMatches() {
runRetraceTest(
"%t %c.%m",
@@ -663,6 +692,7 @@
}
@Test
+ @Ignore("b/165782924")
public void testPruningOfMethodsByFormals() {
runRetraceTest(
"%c.%m\\(%a\\)",
@@ -731,6 +761,41 @@
});
}
+ @Test
+ public void testSourceFileLineNumber() {
+ runRetraceTest(
+ DEFAULT_REGULAR_EXPRESSION.replace("%s(?::%l)?", "%S"),
+ new RetraceAssertionErrorStackTrace());
+ }
+
+ @Test
+ public void testEscaping() {
+ runRetraceTest(
+ "\\%c\\\\%c\\\\\\%c.%m\\(\\\\%S\\)\\\\\\%S",
+ new StackTraceForTest() {
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of("%c\\com.android.tools.r8.Foo\\%c.a(\\SourceFile:1)\\%S");
+ }
+
+ @Override
+ public String mapping() {
+ return "com.android.tools.r8.Bar -> com.android.tools.r8.Foo:\n"
+ + " 1:1:void m():13:13 -> a";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of("%c\\com.android.tools.r8.Bar\\%c.m(\\Bar.java:13)\\%S");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+ });
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(
String regularExpression, StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
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 47dc5c3..8bf3b29 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -23,6 +24,7 @@
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
+import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
@@ -45,12 +47,6 @@
@RunWith(Parameterized.class)
public class RetraceTests extends TestBase {
- // This is a slight modification of the default regular expression shown for proguard retrace
- // that allow for retracing classes in the form <class>: lorem ipsum...
- // Seems like Proguard retrace is expecting the form "Caused by: <class>".
- public static final String DEFAULT_REGULAR_EXPRESSION =
- "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
-
@Parameters(name = "{0}, use regular expression: {1}")
public static Collection<Object[]> data() {
return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
@@ -178,6 +174,11 @@
runRetraceTest(new UnknownSourceStackTrace());
}
+ @Test
+ public void testInlineSourceFileContext() {
+ runRetraceTest(new InlineSourceFileContextStackTrace());
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
index 53aa7c6..d57b024 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
@@ -45,59 +45,6 @@
@Override
public List<String> retracedStackTrace() {
return Arrays.asList(
- "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
- + " because method is not defined on line number 21",
- "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
- + " because method is not defined on line number 21",
- "Pruning \tat com.android.tools.r8.utils.Reporter.fatalError(Reporter.java:21) from result"
- + " because method is not defined on line number 21",
- "Pruning \tat"
- + " com.android.tools.r8.utils.Reporter.addSuppressedExceptions(Reporter.java:21) from"
- + " result because method is not defined on line number 21",
- "Pruning \tat"
- + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
- + " from result because method is not defined on line number 19",
- "Pruning \tat"
- + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
- + " from result because method is not defined on line number 19",
- "Pruning \tat"
- + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
- + " from result because method is not in range on line number 19",
- "Pruning \tat"
- + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
- + " from result because method is not in range on line number 19",
- "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
- + " result because method is not defined on line number 11",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:11)"
- + " from result because method is not defined on line number 11",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:11)"
- + " from result because method is not defined on line number 11",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
- + " from result because method is not defined on line number 11",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
- + " from result because method is not defined on line number 11",
- "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
- + " result because method is not in range on line number 11",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:1)"
- + " from result because method is not defined on line number 1",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:1)"
- + " from result because method is not defined on line number 1",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
- + " from result because method is not defined on line number 1",
- "Pruning \tat"
- + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
- + " from result because method is not defined on line number 1",
- "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
- + " result because method is not defined on line number 1",
- "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
- + " result because method is not defined on line number 1",
"com.android.tools.r8.CompilationFailedException: Compilation failed to complete",
"\tat com.android.tools.r8.BaseCommand$Builder.build(BaseCommand.java:143)",
"\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:104)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
index 4d9418c..ce7a8f5 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
@@ -27,7 +27,7 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
- "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main(java.lang.String[],"
+ "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main(java.lang.String[],"
+ "com.android.Bar)(Main.java:102)");
}
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
new file mode 100644
index 0000000..3c22097
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
@@ -0,0 +1,54 @@
+// 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 InlineSourceFileContextStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ + ".main(KotlinJavaSourceFileTestObject.java:1)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.joinLines(
+ "com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary ->"
+ + " com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary:",
+ "# {\"id\":\"sourceFile\",\"fileName\":\"KotlinJavaSourceFileTestLibrary.kt\"}",
+ " void <init>() -> <init>",
+ "com.google.appreduce.remapper.KotlinJavaSourceFileTestObject ->"
+ + " com.google.appreduce.remapper.KotlinJavaSourceFileTestObject:",
+ " void <init>() -> <init>",
+ " 1:1:void com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".throwsException():22:22 -> main",
+ " 1:1:void com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".callsThrowsException():19 -> main",
+ " 1:1:void main(java.lang.String[]):32 -> main",
+ " 2:7:void printStackTraceUpToMain(java.lang.Exception):19:24 -> main",
+ " 2:7:void main(java.lang.String[]):34 -> main");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".throwsException(KotlinJavaSourceFileTestLibrary.kt:22)",
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.kt:19)",
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ + ".main(KotlinJavaSourceFileTestObject.java:32)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
new file mode 100644
index 0000000..2732b34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
@@ -0,0 +1,47 @@
+// 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 java.util.Arrays;
+import java.util.List;
+
+public class PGStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt"
+ + " to invoke virtual method 'boolean"
+ + " com.google.android.foo(com.google.android.foo.Data$Key)' on a null object"
+ + " reference",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(PG:586)",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.Controller.onToolbarStateChanged(PG:1087)");
+ }
+
+ @Override
+ public String mapping() {
+ return "";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt"
+ + " to invoke virtual method 'boolean"
+ + " com.google.android.foo(com.google.android.foo.Data$Key)' on a null object"
+ + " reference",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(SectionHeaderListController.java:586)",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.Controller.onToolbarStateChanged(Controller.java:1087)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
index 3eb9710..5a47079 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
@@ -25,7 +25,7 @@
"com.android.tools.r8.naming.retrace.Main -> a.a:",
" com.android.Foo main(java.lang.String[],com.android.Bar) -> a",
" com.android.Foo main(java.lang.String[]) -> b",
- " com.android.Bar main(com.android.Bar) -> b");
+ " void main(com.android.Bar) -> b");
}
@Override
@@ -33,11 +33,11 @@
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
"\tat com.android.tools.r8.naming.retrace.Main.c(Main.java)",
- "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+ "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main("
+ "java.lang.String[])(Main.java)",
- "\t<OR> at com.android.Bar com.android.tools.r8.naming.retrace.Main.main("
+ "\t<OR> at com.android.tools.r8.naming.retrace.Main.void main("
+ "com.android.Bar)(Main.java)",
- "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+ "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main("
+ "java.lang.String[],com.android.Bar)(Main.java)");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 3cfbb22..d2c22e5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -37,8 +37,8 @@
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.RetraceBase;
-import com.android.tools.r8.retrace.RetraceBaseImpl;
+import com.android.tools.r8.retrace.RetraceApi;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -471,7 +471,7 @@
}
}
- public RetraceBase retrace() {
- return RetraceBaseImpl.create(mapping);
+ public RetraceApi retrace() {
+ return Retracer.create(mapping);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index c5ca6c9..c3c961a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.retrace.RetraceBase;
+import com.android.tools.r8.retrace.RetraceApi;
import com.android.tools.r8.retrace.RetraceMethodResult;
public interface InstructionSubject {
@@ -125,18 +125,18 @@
return lineNumberTable == null ? -1 : lineNumberTable.getLineForInstruction(this);
}
- default RetraceMethodResult retrace(RetraceBase retraceBase) {
+ default RetraceMethodResult retrace(RetraceApi retraceApi) {
MethodSubject methodSubject = getMethodSubject();
assert methodSubject.isPresent();
- return retraceBase.retrace(methodSubject.asFoundMethodSubject().asMethodReference());
+ return retraceApi.retrace(methodSubject.asFoundMethodSubject().asMethodReference());
}
- default RetraceMethodResult retraceLinePosition(RetraceBase retraceBase) {
- return retrace(retraceBase).narrowByLine(getLineNumber());
+ default RetraceMethodResult retraceLinePosition(RetraceApi retraceApi) {
+ return retrace(retraceApi).narrowByLine(getLineNumber());
}
default RetraceMethodResult retracePcPosition(
- RetraceBase retraceBase, MethodSubject methodSubject) {
- return retrace(retraceBase).narrowByLine(getOffset(methodSubject).offset);
+ RetraceApi retraceApi, MethodSubject methodSubject) {
+ return retrace(retraceApi).narrowByLine(getOffset(methodSubject).offset);
}
}