[Retrace] Add support for rewriting frames based on json comments
Change-Id: I2d02387617a2350cb82abf895ccf3dbcbf9f5d82
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 7a0a20c..00028ff 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -8,8 +8,10 @@
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.RewriteFrameMappingInformation;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.ArrayList;
@@ -453,6 +455,16 @@
return false;
}
+ public List<RewriteFrameMappingInformation> getRewriteFrameMappingInformation() {
+ ImmutableList.Builder<RewriteFrameMappingInformation> builder = ImmutableList.builder();
+ for (MappingInformation mappingInformation : additionalMappingInfo) {
+ if (mappingInformation.isRewriteFrameMappingInformation()) {
+ builder.add(mappingInformation.asRewriteFrameMappingInformation());
+ }
+ }
+ return builder.build();
+ }
+
public int getOriginalLineNumber(int lineNumberAfterMinification) {
if (minifiedRange == null) {
// General mapping without concrete line numbers: "a() -> b"
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
index 13bcfe9..9232500 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -30,6 +30,10 @@
return false;
}
+ public boolean isRewriteFrameMappingInformation() {
+ return false;
+ }
+
public boolean isCompilerSynthesizedMappingInformation() {
return false;
}
@@ -50,6 +54,10 @@
return null;
}
+ public RewriteFrameMappingInformation asRewriteFrameMappingInformation() {
+ return null;
+ }
+
public abstract boolean allowOther(MappingInformation information);
public static void fromJsonObject(
@@ -102,6 +110,9 @@
case CompilerSynthesizedMappingInformation.ID:
CompilerSynthesizedMappingInformation.deserialize(version, onMappingInfo);
return;
+ case RewriteFrameMappingInformation.ID:
+ RewriteFrameMappingInformation.deserialize(version, object, onMappingInfo);
+ return;
default:
diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, id));
UnknownJsonMappingInformation.deserialize(id, object, onMappingInfo);
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
new file mode 100644
index 0000000..150dc45
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
@@ -0,0 +1,262 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.mappinginformation;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.internal.RetraceStackTraceContextImpl;
+import com.android.tools.r8.retrace.internal.RetraceStackTraceCurrentEvaluationInformation;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class RewriteFrameMappingInformation extends MappingInformation {
+
+ public static final MapVersion SUPPORTED_VERSION = MapVersion.MAP_VERSION_EXPERIMENTAL;
+ public static final String ID = "com.android.tools.r8.rewriteframe";
+ private static final String CONDITIONS_KEY = "conditions";
+ private static final String ACTIONS_KEY = "actions";
+
+ private final List<Condition> conditions;
+ private final List<RewriteAction> actions;
+
+ private RewriteFrameMappingInformation(List<Condition> conditions, List<RewriteAction> actions) {
+ this.conditions = conditions;
+ this.actions = actions;
+ }
+
+ public List<Condition> getConditions() {
+ return conditions;
+ }
+
+ public List<RewriteAction> getActions() {
+ return actions;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public String serialize() {
+ JsonObject object = new JsonObject();
+ object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+ JsonArray conditionsArray = new JsonArray();
+ conditions.forEach(condition -> conditionsArray.add(condition.serialize()));
+ object.add(CONDITIONS_KEY, conditionsArray);
+ JsonArray actionsArray = new JsonArray();
+ actions.forEach(action -> actionsArray.add(action.serialize()));
+ object.add(ACTIONS_KEY, actionsArray);
+ return object.toString();
+ }
+
+ public static boolean isSupported(MapVersion version) {
+ return version.isGreaterThanOrEqualTo(SUPPORTED_VERSION);
+ }
+
+ @Override
+ public boolean allowOther(MappingInformation information) {
+ return !information.isRewriteFrameMappingInformation();
+ }
+
+ public static void deserialize(
+ MapVersion mapVersion, JsonObject object, Consumer<MappingInformation> onMappingInfo) {
+ if (!isSupported(mapVersion)) {
+ return;
+ }
+ ImmutableList.Builder<Condition> conditions = ImmutableList.builder();
+ object
+ .get(CONDITIONS_KEY)
+ .getAsJsonArray()
+ .forEach(
+ element -> {
+ conditions.add(Condition.deserialize(element));
+ });
+ ImmutableList.Builder<RewriteAction> actions = ImmutableList.builder();
+ object
+ .get(ACTIONS_KEY)
+ .getAsJsonArray()
+ .forEach(element -> actions.add(RewriteAction.deserialize(element)));
+ onMappingInfo.accept(new RewriteFrameMappingInformation(conditions.build(), actions.build()));
+ }
+
+ @Override
+ public boolean isRewriteFrameMappingInformation() {
+ return true;
+ }
+
+ @Override
+ public RewriteFrameMappingInformation asRewriteFrameMappingInformation() {
+ return this;
+ }
+
+ public abstract static class Condition {
+
+ protected abstract JsonPrimitive serialize();
+
+ private static Condition deserialize(JsonElement element) {
+ String elementString = element.getAsString();
+ int argIndex = elementString.indexOf('(');
+ if (argIndex < 1 || !elementString.endsWith(")")) {
+ throw new CompilationError("Invalid formatted condition: " + elementString);
+ }
+ String functionName = elementString.substring(0, argIndex);
+ String contents = elementString.substring(argIndex + 1, elementString.length() - 1);
+ if (ThrowsCondition.FUNCTION_NAME.equals(functionName)) {
+ return ThrowsCondition.deserialize(contents);
+ }
+ throw new Unimplemented("Unexpected condition: " + elementString);
+ }
+
+ public boolean isThrowsCondition() {
+ return false;
+ }
+
+ public ThrowsCondition asThrowsCondition() {
+ return null;
+ }
+
+ public abstract boolean evaluate(RetraceStackTraceContextImpl context);
+ }
+
+ public static class ThrowsCondition extends Condition {
+
+ static final String FUNCTION_NAME = "throws";
+
+ private final String descriptor;
+
+ private ThrowsCondition(String descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ protected JsonPrimitive serialize() {
+ return new JsonPrimitive(FUNCTION_NAME + "(" + asThrowsCondition().getDescriptor() + ")");
+ }
+
+ @Override
+ public boolean isThrowsCondition() {
+ return true;
+ }
+
+ public String getDescriptor() {
+ return descriptor;
+ }
+
+ @Override
+ public ThrowsCondition asThrowsCondition() {
+ return this;
+ }
+
+ @Override
+ public boolean evaluate(RetraceStackTraceContextImpl context) {
+ return context.getSeenException() != null
+ && context.getSeenException().getDescriptor().equals(descriptor);
+ }
+
+ public static ThrowsCondition deserialize(String conditionString) {
+ if (!DescriptorUtils.isClassDescriptor(conditionString)) {
+ throw new CompilationError("Unexpected throws-descriptor: " + conditionString);
+ }
+ return new ThrowsCondition(conditionString);
+ }
+ }
+
+ public abstract static class RewriteAction {
+
+ static final String REMOVE_INNER_FRAMES_SERIALIZED_NAME = "removeInnerFrames";
+
+ private static final String FUNCTION_KEY = "function";
+ private static final String ARGUMENTS_KEY = "arguments";
+
+ abstract String serializeName();
+
+ abstract JsonArray getArguments();
+
+ JsonElement serialize() {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add(FUNCTION_KEY, new JsonPrimitive(serializeName()));
+ jsonObject.add(ARGUMENTS_KEY, getArguments());
+ return jsonObject;
+ }
+
+ private static RewriteAction deserialize(JsonElement element) {
+ String functionString = element.getAsString();
+ int startArgsIndex = functionString.indexOf("(");
+ int endArgsIndex = functionString.indexOf(")");
+ if (endArgsIndex <= startArgsIndex) {
+ throw new Unimplemented("Unexpected action: " + functionString);
+ }
+ String functionName = functionString.substring(0, startArgsIndex);
+ String args = functionString.substring(startArgsIndex + 1, endArgsIndex);
+ if (REMOVE_INNER_FRAMES_SERIALIZED_NAME.equals(functionName)) {
+ return RemoveInnerFramesAction.create(args);
+ }
+ assert false : "Unknown function " + functionName;
+ throw new Unimplemented("Unexpected action: " + functionName);
+ }
+
+ public boolean isRemoveInnerFramesAction() {
+ return false;
+ }
+
+ public RemoveInnerFramesAction asRemoveInnerFramesRewriteAction() {
+ return null;
+ }
+
+ public abstract void evaluate(RetraceStackTraceCurrentEvaluationInformation.Builder builder);
+ }
+
+ public static class RemoveInnerFramesAction extends RewriteAction {
+
+ private final int numberOfFrames;
+
+ public RemoveInnerFramesAction(int numberOfFrames) {
+ this.numberOfFrames = numberOfFrames;
+ }
+
+ public int getNumberOfFrames() {
+ return numberOfFrames;
+ }
+
+ @Override
+ String serializeName() {
+ return REMOVE_INNER_FRAMES_SERIALIZED_NAME;
+ }
+
+ @Override
+ JsonArray getArguments() {
+ JsonArray arguments = new JsonArray();
+ arguments.add(numberOfFrames);
+ return arguments;
+ }
+
+ static RemoveInnerFramesAction create(String args) {
+ return new RemoveInnerFramesAction(Integer.parseInt(args));
+ }
+
+ @Override
+ public boolean isRemoveInnerFramesAction() {
+ return true;
+ }
+
+ @Override
+ public RemoveInnerFramesAction asRemoveInnerFramesRewriteAction() {
+ return this;
+ }
+
+ @Override
+ public void evaluate(RetraceStackTraceCurrentEvaluationInformation.Builder builder) {
+ builder.incrementRemoveInnerFramesCount(numberOfFrames);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
index 43bf109..6e61118 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassElement.java
@@ -31,4 +31,6 @@
RetraceFrameResult lookupFrame(Optional<Integer> position, MethodReference methodReference);
RetraceUnknownJsonMappingInformationResult getUnknownJsonMappingInformation();
+
+ RetraceStackTraceContext getContextWhereClassWasThrown();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
index 63d40db..55de558 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceElement.java
@@ -16,6 +16,4 @@
R getRetraceResultContext();
boolean isCompilerSynthesized();
-
- RetraceStackTraceContext getContext();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
index b114240..f2ad4e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFrameElement.java
@@ -18,9 +18,12 @@
void visitAllFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
- void visitNonCompilerSynthesizedFrames(BiConsumer<RetracedMethodReference, Integer> consumer);
+ void visitRewrittenFrames(
+ RetraceStackTraceContext context, BiConsumer<RetracedMethodReference, Integer> consumer);
RetracedSourceFile getSourceFile(RetracedClassMemberReference frame);
List<? extends RetracedMethodReference> getOuterFrames();
+
+ RetraceStackTraceContext getContext();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java
new file mode 100644
index 0000000..ace1b86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidRewriteFrameDiagnostics.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+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;
+
+@Keep
+public class RetraceInvalidRewriteFrameDiagnostics implements Diagnostic {
+
+ private final int numberOfFramesToRemove;
+ private final String method;
+
+ private RetraceInvalidRewriteFrameDiagnostics(int numberOfFramesToRemove, String method) {
+ this.numberOfFramesToRemove = numberOfFramesToRemove;
+ this.method = method;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Cannot remove "
+ + numberOfFramesToRemove
+ + " frames from the retraced output of "
+ + method
+ + " because it exceeds the number of retraced frames";
+ }
+
+ public static RetraceInvalidRewriteFrameDiagnostics create(
+ int numberOfFramesToRemove, String method) {
+ return new RetraceInvalidRewriteFrameDiagnostics(numberOfFramesToRemove, method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
index 9004cc1..e87ff95 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceContext.java
@@ -11,6 +11,6 @@
public interface RetraceStackTraceContext {
static RetraceStackTraceContext getInitialContext() {
- return new RetraceStackTraceContextImpl();
+ return RetraceStackTraceContextImpl.builder().build();
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index c609e44..bd865a8 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -33,4 +33,9 @@
ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
return RetracerImpl.create(proguardMapProducer, diagnosticsHandler, false);
}
+
+ static Retracer createExperimental(
+ ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+ return RetracerImpl.create(proguardMapProducer, diagnosticsHandler, true);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index 54c90e6..0f165cf 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetraceUnknownJsonMappingInformationResult;
import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
@@ -220,7 +219,7 @@
RetraceClassResultImpl classResult,
List<Pair<RetraceClassElementImpl, T>> mappings,
D definition,
- Retracer retracer);
+ RetracerImpl retracer);
}
public static class RetraceClassElementImpl implements RetraceClassElement {
@@ -273,9 +272,10 @@
}
@Override
- public RetraceStackTraceContext getContext() {
- // TODO(b/197936862): Extend the context to enable tracking information.
- return RetraceStackTraceContext.getInitialContext();
+ public RetraceStackTraceContext getContextWhereClassWasThrown() {
+ return RetraceStackTraceContextImpl.builder()
+ .setSeenException(getRetracedClass().getClassReference())
+ .build();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index b536bea..38667c4 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceFieldElement;
import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedSourceFile;
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
@@ -114,11 +113,6 @@
}
@Override
- public RetraceStackTraceContext getContext() {
- return RetraceStackTraceContext.getInitialContext();
- }
-
- @Override
public boolean isUnknown() {
return fieldReference.isUnknown();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index 2f79d8e..9bee685 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -6,16 +6,17 @@
import static com.android.tools.r8.retrace.internal.RetraceUtils.methodReferenceFromMappedRange;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.Range;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceFrameResult;
+import com.android.tools.r8.retrace.RetraceInvalidRewriteFrameDiagnostics;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedClassMemberReference;
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -35,7 +36,7 @@
private final MethodDefinition methodDefinition;
private final Optional<Integer> obfuscatedPosition;
private final List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges;
- private final Retracer retracer;
+ private final RetracerImpl retracer;
private OptionalBool isAmbiguousCache = OptionalBool.UNKNOWN;
@@ -44,7 +45,7 @@
List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges,
MethodDefinition methodDefinition,
Optional<Integer> obfuscatedPosition,
- Retracer retracer) {
+ RetracerImpl retracer) {
this.classResult = classResult;
this.methodDefinition = methodDefinition;
this.obfuscatedPosition = obfuscatedPosition;
@@ -93,7 +94,8 @@
methodDefinition.substituteHolder(
classElement.getRetracedClass().getClassReference())),
ImmutableList.of(),
- obfuscatedPosition));
+ obfuscatedPosition,
+ retracer));
}
// Iterate over mapped ranges that may have different positions than specified.
List<ElementImpl> ambiguousFrames = new ArrayList<>();
@@ -125,7 +127,8 @@
classElement,
getRetracedMethod(methodReference, topFrame, obfuscatedPosition),
mappedRangesForElement,
- obfuscatedPosition);
+ obfuscatedPosition,
+ retracer);
}
private RetracedMethodReferenceImpl getRetracedMethod(
@@ -156,18 +159,21 @@
private final RetraceClassElementImpl classElement;
private final List<MappedRange> mappedRanges;
private final Optional<Integer> obfuscatedPosition;
+ private final RetracerImpl retracer;
- private ElementImpl(
+ ElementImpl(
RetraceFrameResultImpl retraceFrameResult,
RetraceClassElementImpl classElement,
RetracedMethodReferenceImpl methodReference,
List<MappedRange> mappedRanges,
- Optional<Integer> obfuscatedPosition) {
+ Optional<Integer> obfuscatedPosition,
+ RetracerImpl retracer) {
this.methodReference = methodReference;
this.retraceFrameResult = retraceFrameResult;
this.classElement = classElement;
this.mappedRanges = mappedRanges;
this.obfuscatedPosition = obfuscatedPosition;
+ this.retracer = retracer;
}
private boolean isOuterMostFrameCompilerSynthesized() {
@@ -188,11 +194,6 @@
}
@Override
- public RetraceStackTraceContext getContext() {
- return RetraceStackTraceContext.getInitialContext();
- }
-
- @Override
public RetraceFrameResult getRetraceResultContext() {
return retraceFrameResult;
}
@@ -222,17 +223,32 @@
}
@Override
- public void visitNonCompilerSynthesizedFrames(
- BiConsumer<RetracedMethodReference, Integer> consumer) {
+ public void visitRewrittenFrames(
+ RetraceStackTraceContext context, BiConsumer<RetracedMethodReference, Integer> consumer) {
+ RetraceStackTraceContextImpl contextImpl = (RetraceStackTraceContextImpl) context;
+ RetraceStackTraceCurrentEvaluationInformation currentFrameInformation =
+ contextImpl.computeRewritingInformation(mappedRanges);
int index = 0;
+ int numberOfFramesToRemove = currentFrameInformation.getRemoveInnerFrames();
RetracedMethodReferenceImpl prev = getTopFrame();
- for (RetracedMethodReferenceImpl next : getOuterFrames()) {
- consumer.accept(prev, index++);
+ List<RetracedMethodReferenceImpl> outerFrames = getOuterFrames();
+ if (numberOfFramesToRemove > outerFrames.size() + 1) {
+ assert prev.isKnown();
+ DiagnosticsHandler diagnosticsHandler = retracer.getDiagnosticsHandler();
+ diagnosticsHandler.warning(
+ RetraceInvalidRewriteFrameDiagnostics.create(
+ numberOfFramesToRemove, prev.asKnown().toString()));
+ numberOfFramesToRemove = 0;
+ }
+ for (RetracedMethodReferenceImpl next : outerFrames) {
+ if (numberOfFramesToRemove-- <= 0) {
+ consumer.accept(prev, index++);
+ }
prev = next;
}
// We expect only the last frame, i.e., the outer-most caller to potentially be synthesized.
// If not include it too.
- if (!isOuterMostFrameCompilerSynthesized()) {
+ if (numberOfFramesToRemove <= 0 && !isOuterMostFrameCompilerSynthesized()) {
consumer.accept(prev, index);
}
}
@@ -259,5 +275,11 @@
}
return outerFrames;
}
+
+ @Override
+ public RetraceStackTraceContext getContext() {
+ // This will change when supporting outline frames.
+ return RetraceStackTraceContext.getInitialContext();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 8ccdbdb..7d97ed2 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -10,10 +10,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.retrace.RetraceMethodElement;
import com.android.tools.r8.retrace.RetraceMethodResult;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
@@ -27,13 +25,13 @@
private final MethodDefinition methodDefinition;
private final RetraceClassResultImpl classResult;
private final List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges;
- private final Retracer retracer;
+ private final RetracerImpl retracer;
RetraceMethodResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges,
MethodDefinition methodDefinition,
- Retracer retracer) {
+ RetracerImpl retracer) {
this.classResult = classResult;
this.mappedRanges = mappedRanges;
this.methodDefinition = methodDefinition;
@@ -149,11 +147,6 @@
}
@Override
- public RetraceStackTraceContext getContext() {
- return RetraceStackTraceContext.getInitialContext();
- }
-
- @Override
public boolean isUnknown() {
return methodReference.isUnknown();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
index b33e06d..7cc4a30 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
@@ -4,6 +4,72 @@
package com.android.tools.r8.retrace.internal;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.Condition;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RewriteAction;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.List;
-public class RetraceStackTraceContextImpl implements RetraceStackTraceContext {}
+public class RetraceStackTraceContextImpl implements RetraceStackTraceContext {
+
+ private final ClassReference seenException;
+
+ private RetraceStackTraceContextImpl(ClassReference seenException) {
+ this.seenException = seenException;
+ }
+
+ public ClassReference getSeenException() {
+ return seenException;
+ }
+
+ RetraceStackTraceCurrentEvaluationInformation computeRewritingInformation(
+ List<MappedRange> mappedRanges) {
+ if (mappedRanges == null || mappedRanges.isEmpty()) {
+ return RetraceStackTraceCurrentEvaluationInformation.empty();
+ }
+ RetraceStackTraceCurrentEvaluationInformation.Builder builder =
+ RetraceStackTraceCurrentEvaluationInformation.builder();
+ MappedRange last = ListUtils.last(mappedRanges);
+ for (RewriteFrameMappingInformation rewriteInformation :
+ last.getRewriteFrameMappingInformation()) {
+ if (evaluateConditions(rewriteInformation.getConditions())) {
+ for (RewriteAction action : rewriteInformation.getActions()) {
+ action.evaluate(builder);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ private boolean evaluateConditions(List<Condition> conditions) {
+ for (Condition condition : conditions) {
+ if (!condition.evaluate(this)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private ClassReference seenException;
+
+ private Builder() {}
+
+ public Builder setSeenException(ClassReference seenException) {
+ this.seenException = seenException;
+ return this;
+ }
+
+ public RetraceStackTraceContextImpl build() {
+ return new RetraceStackTraceContextImpl(seenException);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java
new file mode 100644
index 0000000..b25454f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceCurrentEvaluationInformation.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+public class RetraceStackTraceCurrentEvaluationInformation {
+
+ private static final RetraceStackTraceCurrentEvaluationInformation EMPTY =
+ new RetraceStackTraceCurrentEvaluationInformation(0);
+
+ private final int removeInnerFrames;
+
+ private RetraceStackTraceCurrentEvaluationInformation(int removeInnerFrames) {
+ this.removeInnerFrames = removeInnerFrames;
+ }
+
+ public int getRemoveInnerFrames() {
+ return removeInnerFrames;
+ }
+
+ public static RetraceStackTraceCurrentEvaluationInformation empty() {
+ return EMPTY;
+ }
+
+ public static RetraceStackTraceCurrentEvaluationInformation.Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private int removeInnerFramesCount;
+
+ public Builder incrementRemoveInnerFramesCount(int increment) {
+ removeInnerFramesCount += increment;
+ return this;
+ }
+
+ RetraceStackTraceCurrentEvaluationInformation build() {
+ return new RetraceStackTraceCurrentEvaluationInformation(removeInnerFramesCount);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index 0758be3..bad33d8 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -21,9 +21,11 @@
public class RetracerImpl implements Retracer {
private final ClassNameMapper classNameMapper;
+ private final DiagnosticsHandler diagnosticsHandler;
- public RetracerImpl(ClassNameMapper classNameMapper) {
+ public RetracerImpl(ClassNameMapper classNameMapper, DiagnosticsHandler diagnosticsHandler) {
this.classNameMapper = classNameMapper;
+ this.diagnosticsHandler = diagnosticsHandler;
assert classNameMapper != null;
}
@@ -33,7 +35,8 @@
boolean allowExperimentalMapping) {
if (proguardMapProducer instanceof DirectClassNameMapperProguardMapProducer) {
return new RetracerImpl(
- ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper());
+ ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper(),
+ diagnosticsHandler);
}
try {
ClassNameMapper classNameMapper =
@@ -42,12 +45,16 @@
diagnosticsHandler,
true,
allowExperimentalMapping);
- return new RetracerImpl(classNameMapper);
+ return new RetracerImpl(classNameMapper, diagnosticsHandler);
} catch (Throwable throwable) {
throw new InvalidMappingFileException(throwable);
}
}
+ public DiagnosticsHandler getDiagnosticsHandler() {
+ return diagnosticsHandler;
+ }
+
@Override
public RetraceMethodResultImpl retraceMethod(MethodReference methodReference) {
return retraceClass(methodReference.getHolderClass())
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index 9fe783b..2e2c38f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -78,7 +78,9 @@
.setRetracedClass(classElement.getRetracedClass())
.joinAmbiguous(classResult.isAmbiguous())
.setTopFrame(true)
- .setContext(classElement.getContext())
+ // We assume, since no method was defined for this stack trace element,
+ // that this was a thrown exception.
+ .setContext(classElement.getContextWhereClassWasThrown())
.applyIf(
element.hasSourceFile(),
builder -> {
@@ -110,7 +112,8 @@
frameElement -> {
List<RetraceStackTraceElementProxyImpl<T, ST>> retracedProxies =
new ArrayList<>();
- frameElement.visitNonCompilerSynthesizedFrames(
+ frameElement.visitRewrittenFrames(
+ proxy.getContext(),
(frame, index) -> {
boolean isTopFrame = index == 0;
retracedProxies.add(
@@ -164,7 +167,6 @@
.setRetracedField(fieldElement.getField())
.joinAmbiguous(retraceFieldResult.isAmbiguous())
.setTopFrame(true)
- .setContext(fieldElement.getContext())
.applyIf(
element.hasSourceFile(),
builder -> {
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 3a8fd5b..d1a074b 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
import com.android.tools.r8.retrace.stacktraces.NoObfuscatedLineNumberWithOverrideTest;
import com.android.tools.r8.retrace.stacktraces.NoObfuscationRangeMappingWithStackTrace;
+import com.android.tools.r8.retrace.stacktraces.NpeInlineRetraceStackTrace;
import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfuscatedRangeToSingleLineStackTrace;
@@ -309,6 +310,11 @@
runRetraceTest(new AmbiguousWithSignatureVerboseStackTrace());
}
+ @Test
+ public void testNpeInlineRetraceStackTrace() throws Exception {
+ runExperimentalRetraceTest(new NpeInlineRetraceStackTrace());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
new file mode 100644
index 0000000..0730508
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceClassElement;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.Retracer;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiRewriteFrameInlineNpeTest extends RetraceApiTestBase {
+
+ public RetraceApiRewriteFrameInlineNpeTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final String npeDescriptor = "Ljava/lang/NullPointerException;";
+
+ private final String mapping =
+ "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }\n"
+ + "some.Class -> a:\n"
+ + " 4:4:void other.Class.inlinee():23:23 -> a\n"
+ + " 4:4:void caller(other.Class):7 -> a\n"
+ + " # { id: 'com.android.tools.r8.rewriteframe', "
+ + " conditions: ['throws("
+ + npeDescriptor
+ + ")'], "
+ + " actions: ['removeInnerFrames(1)']"
+ + " }";
+
+ @Test
+ public void testFirstStackLineIsRemoved() {
+ TestDiagnosticsHandler testDiagnosticsHandler = new TestDiagnosticsHandler();
+ Retracer retracer =
+ Retracer.createExperimental(
+ ProguardMapProducer.fromString(mapping), testDiagnosticsHandler);
+
+ List<RetraceClassElement> npeRetraced =
+ retracer.retraceClass(Reference.classFromDescriptor(npeDescriptor)).stream()
+ .collect(Collectors.toList());
+ assertEquals(1, npeRetraced.size());
+
+ RetraceStackTraceContext context = npeRetraced.get(0).getContextWhereClassWasThrown();
+
+ List<RetraceFrameElement> retraceFrameElements =
+ retracer.retraceClass(Reference.classFromTypeName("a")).stream()
+ .flatMap(element -> element.lookupFrame(Optional.of(4), "a").stream())
+ .collect(Collectors.toList());
+ assertEquals(1, retraceFrameElements.size());
+
+ RetraceFrameElement retraceFrameElement = retraceFrameElements.get(0);
+ // Check that rewriting the frames will remove the top 1 frames if the condition is active.
+ Map<Integer, RetracedMethodReference> results = new LinkedHashMap<>();
+ retraceFrameElement.visitRewrittenFrames(
+ context,
+ (methodReference, index) -> {
+ RetracedMethodReference existingValue = results.put(index, methodReference);
+ assertNull(existingValue);
+ });
+ assertEquals(1, results.size());
+ assertEquals(7, results.get(0).getOriginalPositionOrDefault(4));
+ assertEquals(results.get(0).getMethodName(), "caller");
+ }
+
+ private static class TestDiagnosticsHandler implements com.android.tools.r8.DiagnosticsHandler {
+
+ private List<Diagnostic> infoMessages = new ArrayList<>();
+
+ @Override
+ public void warning(Diagnostic warning) {
+ throw new RuntimeException("Warning not expected");
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ throw new RuntimeException("Error not expected");
+ }
+
+ @Override
+ public void info(Diagnostic info) {
+ DiagnosticsHandler.super.info(info);
+ infoMessages.add(info);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
index e1ad580..376a274 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedFrameTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.ProguardMapProducer;
import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.Retracer;
import java.util.ArrayList;
@@ -57,7 +58,8 @@
retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
assertEquals(2, allFrames.size());
List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
- retraceFrameElement.visitNonCompilerSynthesizedFrames(
+ retraceFrameElement.visitRewrittenFrames(
+ RetraceStackTraceContext.getInitialContext(),
(method, ignored) -> nonSyntheticFrames.add(method));
assertEquals(1, nonSyntheticFrames.size());
assertEquals(nonSyntheticFrames.get(0), allFrames.get(0));
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
index 73367a6..c7b8d7a 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.ProguardMapProducer;
import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.Retracer;
import java.util.ArrayList;
@@ -57,7 +58,8 @@
retraceFrameElement.visitAllFrames((method, ignored) -> allFrames.add(method));
assertEquals(2, allFrames.size());
List<RetracedMethodReference> nonSyntheticFrames = new ArrayList<>();
- retraceFrameElement.visitNonCompilerSynthesizedFrames(
+ retraceFrameElement.visitRewrittenFrames(
+ RetraceStackTraceContext.getInitialContext(),
(method, ignored) -> nonSyntheticFrames.add(method));
assertEquals(allFrames, nonSyntheticFrames);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java
new file mode 100644
index 0000000..04528c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NpeInlineRetraceStackTrace.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class NpeInlineRetraceStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException", "\tat a.a(:4)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }",
+ "some.Class -> a:",
+ " 4:4:void other.Class():23:23 -> a",
+ " 4:4:void caller(other.Class):7 -> a",
+ " # { id: 'com.android.tools.r8.rewriteframe', "
+ + "conditions: ['throws(Ljava/lang/NullPointerException;)'], "
+ + "actions: ['removeInnerFrames(1)'] }");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.caller(Class.java:7)");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.void caller(other.Class)(Class.java:7)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
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 76d76e5..d753ca5 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
@@ -177,7 +177,7 @@
public Retracer getRetracer() {
if (lazyRetracer == null) {
- lazyRetracer = new RetracerImpl(mapping);
+ lazyRetracer = new RetracerImpl(mapping, new TestDiagnosticMessagesImpl());
}
return lazyRetracer;
}