blob: 9fe783bc6dcd28fd23a241ab4e982738c4af77c3 [file] [log] [blame]
// 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.internal;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceClassResult;
import com.android.tools.r8.retrace.RetraceFieldResult;
import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
import com.android.tools.r8.retrace.RetraceTypeResult;
import com.android.tools.r8.retrace.RetraceTypeResult.Element;
import com.android.tools.r8.retrace.RetracedClassReference;
import com.android.tools.r8.retrace.RetracedFieldReference;
import com.android.tools.r8.retrace.RetracedMethodReference;
import com.android.tools.r8.retrace.RetracedSourceFile;
import com.android.tools.r8.retrace.RetracedTypeReference;
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.retrace.StackTraceElementProxyRetracer;
import com.android.tools.r8.utils.ListUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StackTraceElementProxyRetracerImpl<T, ST extends StackTraceElementProxy<T, ST>>
implements StackTraceElementProxyRetracer<T, ST> {
private final Retracer retracer;
public StackTraceElementProxyRetracerImpl(Retracer retracer) {
this.retracer = retracer;
}
@Override
public Stream<? extends RetraceStackTraceElementProxy<T, ST>> retrace(
ST element, RetraceStackTraceContext context) {
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults =
Stream.of(RetraceStackTraceElementProxyImpl.create(element, context));
if (!element.hasClassName()
&& !element.hasFieldOrReturnType()
&& !element.hasMethodArguments()) {
return currentResults;
}
currentResults = retraceFieldOrReturnType(currentResults, element);
currentResults = retracedMethodArguments(currentResults, element);
if (element.hasClassName()) {
RetraceClassResult classResult = retracer.retraceClass(element.getClassReference());
if (element.hasMethodName()) {
currentResults = retraceMethod(currentResults, element, classResult);
} else if (element.hasFieldName()) {
currentResults = retraceField(currentResults, element, classResult);
} else {
currentResults = retraceClassOrType(currentResults, element, classResult);
}
}
return currentResults;
}
private Stream<RetraceStackTraceElementProxyImpl<T, ST>> retraceClassOrType(
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults,
ST element,
RetraceClassResult classResult) {
return currentResults.flatMap(
proxy ->
classResult.stream()
.map(
classElement ->
proxy
.builder()
.setRetracedClass(classElement.getRetracedClass())
.joinAmbiguous(classResult.isAmbiguous())
.setTopFrame(true)
.setContext(classElement.getContext())
.applyIf(
element.hasSourceFile(),
builder -> {
RetracedSourceFile sourceFile = classElement.getSourceFile();
builder.setSourceFile(
sourceFile.hasRetraceResult()
? sourceFile.getSourceFile()
: RetraceUtils.inferSourceFile(
classElement.getRetracedClass().getTypeName(),
element.getSourceFile(),
classResult.hasRetraceResult()));
})
.build()));
}
private Stream<RetraceStackTraceElementProxyImpl<T, ST>> retraceMethod(
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults,
ST element,
RetraceClassResult classResult) {
return currentResults.flatMap(
proxy -> {
RetraceFrameResult frameResult =
classResult.lookupFrame(
proxy.context,
element.hasLineNumber() ? Optional.of(element.getLineNumber()) : Optional.empty(),
element.getMethodName());
return frameResult.stream()
.flatMap(
frameElement -> {
List<RetraceStackTraceElementProxyImpl<T, ST>> retracedProxies =
new ArrayList<>();
frameElement.visitNonCompilerSynthesizedFrames(
(frame, index) -> {
boolean isTopFrame = index == 0;
retracedProxies.add(
proxy
.builder()
.setRetracedClass(frame.getHolderClass())
.setRetracedMethod(frame)
.joinAmbiguous(frameResult.isAmbiguous() && isTopFrame)
.setTopFrame(isTopFrame)
.setContext(frameElement.getContext())
.applyIf(
element.hasLineNumber(),
builder -> {
builder.setLineNumber(
frame.getOriginalPositionOrDefault(
element.getLineNumber()));
})
.applyIf(
element.hasSourceFile(),
builder -> {
RetracedSourceFile sourceFileResult =
frameElement.getSourceFile(frame);
builder.setSourceFile(
sourceFileResult.hasRetraceResult()
? sourceFileResult.getSourceFile()
: RetraceUtils.inferSourceFile(
frame.getHolderClass().getTypeName(),
element.getSourceFile(),
classResult.hasRetraceResult()));
})
.build());
});
return retracedProxies.stream();
});
});
}
private Stream<RetraceStackTraceElementProxyImpl<T, ST>> retraceField(
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults,
ST element,
RetraceClassResult classResult) {
return currentResults.flatMap(
proxy -> {
RetraceFieldResult retraceFieldResult = classResult.lookupField(element.getFieldName());
return retraceFieldResult.stream()
.map(
fieldElement ->
proxy
.builder()
.setRetracedClass(fieldElement.getField().getHolderClass())
.setRetracedField(fieldElement.getField())
.joinAmbiguous(retraceFieldResult.isAmbiguous())
.setTopFrame(true)
.setContext(fieldElement.getContext())
.applyIf(
element.hasSourceFile(),
builder -> {
RetracedSourceFile sourceFile = fieldElement.getSourceFile();
builder.setSourceFile(
sourceFile.hasRetraceResult()
? sourceFile.getSourceFile()
: RetraceUtils.inferSourceFile(
fieldElement.getField().getHolderClass().getTypeName(),
element.getSourceFile(),
classResult.hasRetraceResult()));
})
.build());
});
}
private Stream<RetraceStackTraceElementProxyImpl<T, ST>> retraceFieldOrReturnType(
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults, ST element) {
if (!element.hasFieldOrReturnType()) {
return currentResults;
}
String elementOrReturnType = element.getFieldOrReturnType();
if (elementOrReturnType.equals("void")) {
return currentResults.map(
proxy ->
proxy
.builder()
.setRetracedFieldOrReturnType(RetracedTypeReferenceImpl.createVoid())
.build());
} else {
TypeReference typeReference = Reference.typeFromTypeName(elementOrReturnType);
RetraceTypeResult retraceTypeResult = retracer.retraceType(typeReference);
List<Element> retracedElements = retraceTypeResult.stream().collect(Collectors.toList());
return currentResults.flatMap(
proxy ->
retracedElements.stream()
.map(
retracedResult ->
proxy
.builder()
.setRetracedFieldOrReturnType(retracedResult.getType())
.joinAmbiguous(retraceTypeResult.isAmbiguous())
.build()));
}
}
private Stream<RetraceStackTraceElementProxyImpl<T, ST>> retracedMethodArguments(
Stream<RetraceStackTraceElementProxyImpl<T, ST>> currentResults, ST element) {
if (!element.hasMethodArguments()) {
return currentResults;
}
List<RetraceTypeResult> retracedResults =
Arrays.stream(element.getMethodArguments().split(","))
.map(typeName -> retracer.retraceType(Reference.typeFromTypeName(typeName)))
.collect(Collectors.toList());
List<List<RetracedTypeReference>> initial = new ArrayList<>();
initial.add(new ArrayList<>());
List<List<RetracedTypeReference>> allRetracedArguments =
ListUtils.fold(
retracedResults,
initial,
(acc, retracedTypeResult) -> {
List<List<RetracedTypeReference>> newResult = new ArrayList<>();
retracedTypeResult.forEach(
retracedElement ->
acc.forEach(
oldResult -> {
List<RetracedTypeReference> newList = new ArrayList<>(oldResult);
newList.add(retracedElement.getType());
newResult.add(newList);
}));
return newResult;
});
boolean isAmbiguous = allRetracedArguments.size() > 1;
return currentResults.flatMap(
proxy ->
allRetracedArguments.stream()
.map(
retracedArguments ->
proxy
.builder()
.setRetracedMethodArguments(retracedArguments)
.joinAmbiguous(isAmbiguous)
.build()));
}
public static class RetraceStackTraceElementProxyImpl<T, ST extends StackTraceElementProxy<T, ST>>
implements RetraceStackTraceElementProxy<T, ST> {
private final ST originalItem;
private final RetracedClassReference retracedClass;
private final RetracedMethodReference retracedMethod;
private final RetracedFieldReference retracedField;
private final RetracedTypeReference fieldOrReturnType;
private final List<RetracedTypeReference> methodArguments;
private final String sourceFile;
private final int lineNumber;
private final boolean isAmbiguous;
private final boolean isTopFrame;
private final RetraceStackTraceContext context;
private RetraceStackTraceElementProxyImpl(
ST originalItem,
RetracedClassReference retracedClass,
RetracedMethodReference retracedMethod,
RetracedFieldReference retracedField,
RetracedTypeReference fieldOrReturnType,
List<RetracedTypeReference> methodArguments,
String sourceFile,
int lineNumber,
boolean isAmbiguous,
boolean isTopFrame,
RetraceStackTraceContext context) {
assert originalItem != null;
this.originalItem = originalItem;
this.retracedClass = retracedClass;
this.retracedMethod = retracedMethod;
this.retracedField = retracedField;
this.fieldOrReturnType = fieldOrReturnType;
this.methodArguments = methodArguments;
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
this.isAmbiguous = isAmbiguous;
this.isTopFrame = isTopFrame;
this.context = context;
}
@Override
public boolean isAmbiguous() {
return isAmbiguous;
}
@Override
public boolean isTopFrame() {
return isTopFrame;
}
@Override
public boolean hasRetracedClass() {
return retracedClass != null;
}
@Override
public boolean hasRetracedMethod() {
return retracedMethod != null;
}
@Override
public boolean hasRetracedField() {
return retracedField != null;
}
@Override
public boolean hasSourceFile() {
return sourceFile != null;
}
@Override
public boolean hasLineNumber() {
return lineNumber != -1;
}
@Override
public boolean hasFieldOrReturnType() {
return fieldOrReturnType != null;
}
@Override
public boolean hasMethodArguments() {
return methodArguments != null;
}
@Override
public ST getOriginalItem() {
return originalItem;
}
@Override
public RetracedClassReference getRetracedClass() {
return retracedClass;
}
@Override
public RetracedMethodReference getRetracedMethod() {
return retracedMethod;
}
@Override
public RetracedFieldReference getRetracedField() {
return retracedField;
}
@Override
public RetracedTypeReference getRetracedFieldOrReturnType() {
return fieldOrReturnType;
}
@Override
public List<RetracedTypeReference> getMethodArguments() {
return methodArguments;
}
@Override
public String getSourceFile() {
return sourceFile;
}
private static <T, ST extends StackTraceElementProxy<T, ST>>
RetraceStackTraceElementProxyImpl<T, ST> create(
ST originalItem, RetraceStackTraceContext context) {
return new RetraceStackTraceElementProxyImpl<T, ST>(
originalItem, null, null, null, null, null, null, -1, false, false, context);
}
private Builder<T, ST> builder() {
Builder<T, ST> builder = new Builder<>(originalItem);
builder.classContext = retracedClass;
builder.methodContext = retracedMethod;
builder.retracedField = retracedField;
builder.fieldOrReturnType = fieldOrReturnType;
builder.methodArguments = methodArguments;
builder.sourceFile = sourceFile;
builder.lineNumber = lineNumber;
builder.isAmbiguous = isAmbiguous;
builder.isTopFrame = isTopFrame;
builder.context = context;
return builder;
}
@Override
public int getLineNumber() {
return lineNumber;
}
@Override
public RetraceStackTraceContext getContext() {
return context;
}
@Override
public int compareTo(RetraceStackTraceElementProxy<T, ST> other) {
int classCompare = Boolean.compare(hasRetracedClass(), other.hasRetracedClass());
if (classCompare != 0) {
return classCompare;
}
if (hasRetracedClass()) {
classCompare =
getRetracedClass().getTypeName().compareTo(other.getRetracedClass().getTypeName());
if (classCompare != 0) {
return classCompare;
}
}
int methodCompare = Boolean.compare(hasRetracedMethod(), other.hasRetracedMethod());
if (methodCompare != 0) {
return methodCompare;
}
if (hasRetracedMethod()) {
methodCompare = getRetracedMethod().compareTo(other.getRetracedMethod());
if (methodCompare != 0) {
return methodCompare;
}
}
int sourceFileCompare = Boolean.compare(hasSourceFile(), other.hasSourceFile());
if (sourceFileCompare != 0) {
return sourceFileCompare;
}
if (hasSourceFile()) {
sourceFileCompare = getSourceFile().compareTo(other.getSourceFile());
if (sourceFileCompare != 0) {
return sourceFileCompare;
}
}
int lineNumberCompare = Boolean.compare(hasLineNumber(), other.hasLineNumber());
if (lineNumberCompare != 0) {
return lineNumberCompare;
}
if (hasLineNumber()) {
return Integer.compare(lineNumber, other.getLineNumber());
}
return 0;
}
private static class Builder<T, ST extends StackTraceElementProxy<T, ST>> {
private final ST originalElement;
private RetracedClassReference classContext;
private RetracedMethodReference methodContext;
private RetracedFieldReference retracedField;
private RetracedTypeReference fieldOrReturnType;
private List<RetracedTypeReference> methodArguments;
private String sourceFile;
private int lineNumber = -1;
private boolean isAmbiguous;
private boolean isTopFrame;
private RetraceStackTraceContext context;
private Builder(ST originalElement) {
this.originalElement = originalElement;
}
private Builder<T, ST> setRetracedClass(RetracedClassReference retracedClass) {
this.classContext = retracedClass;
return this;
}
private Builder<T, ST> setRetracedMethod(RetracedMethodReference methodElement) {
this.methodContext = methodElement;
return this;
}
private Builder<T, ST> setRetracedField(RetracedFieldReference retracedField) {
this.retracedField = retracedField;
return this;
}
private Builder<T, ST> setRetracedFieldOrReturnType(RetracedTypeReference retracedType) {
this.fieldOrReturnType = retracedType;
return this;
}
private Builder<T, ST> setRetracedMethodArguments(List<RetracedTypeReference> arguments) {
this.methodArguments = arguments;
return this;
}
private Builder<T, ST> setSourceFile(String sourceFile) {
this.sourceFile = sourceFile;
return this;
}
private Builder<T, ST> setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
return this;
}
private Builder<T, ST> joinAmbiguous(boolean ambiguous) {
this.isAmbiguous = ambiguous || this.isAmbiguous;
return this;
}
private Builder<T, ST> setTopFrame(boolean topFrame) {
isTopFrame = topFrame;
return this;
}
private Builder<T, ST> setContext(RetraceStackTraceContext context) {
this.context = context;
return this;
}
private Builder<T, ST> applyIf(boolean condition, Consumer<Builder<T, ST>> consumer) {
if (condition) {
consumer.accept(this);
}
return this;
}
private RetraceStackTraceElementProxyImpl<T, ST> build() {
RetracedClassReference retracedClass = classContext;
if (methodContext != null) {
retracedClass = methodContext.getHolderClass();
}
return new RetraceStackTraceElementProxyImpl<>(
originalElement,
retracedClass,
methodContext,
retracedField,
fieldOrReturnType,
methodArguments,
sourceFile,
lineNumber,
isAmbiguous,
isTopFrame,
context);
}
}
}
}