blob: 5a76e442fffa38942c5d63ccbf5190ee84865c43 [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 static com.android.tools.r8.retrace.internal.PlainStackTraceVisitor.firstNonWhiteSpaceCharacterFromIndex;
import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
import static com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StringIndex.noIndex;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceStackTraceProxy;
import com.android.tools.r8.retrace.RetracedClass;
import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.RetracedType;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.android.tools.r8.utils.TriFunction;
import java.util.ArrayList;
import java.util.List;
public final class StackTraceElementStringProxy extends StackTraceElementProxy<String> {
private final String line;
private final List<StringIndex> orderedIndices;
private final ClassStringIndex className;
private final StringIndex methodName;
private final StringIndex sourceFile;
private final StringIndex lineNumber;
private final StringIndex fieldName;
private final StringIndex fieldOrReturnType;
private final StringIndex methodArguments;
private StackTraceElementStringProxy(
String line,
List<StringIndex> orderedIndices,
ClassStringIndex className,
StringIndex methodName,
StringIndex sourceFile,
StringIndex lineNumber,
StringIndex fieldName,
StringIndex fieldOrReturnType,
StringIndex methodArguments) {
this.line = line;
this.orderedIndices = orderedIndices;
this.className = className;
this.methodName = methodName;
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
this.fieldName = fieldName;
this.fieldOrReturnType = fieldOrReturnType;
this.methodArguments = methodArguments;
}
static StackTraceElementStringProxyBuilder builder(String line) {
return new StackTraceElementStringProxyBuilder(line);
}
@Override
public boolean hasClassName() {
return className.hasIndex();
}
@Override
public boolean hasMethodName() {
return methodName.hasIndex();
}
@Override
public boolean hasFileName() {
return sourceFile.hasIndex();
}
@Override
public boolean hasLineNumber() {
return lineNumber.hasIndex();
}
@Override
public boolean hasFieldName() {
return fieldName.hasIndex();
}
@Override
public boolean hasFieldOrReturnType() {
return fieldOrReturnType.hasIndex();
}
@Override
public boolean hasMethodArguments() {
return methodArguments.hasIndex();
}
@Override
public ClassReference getClassReference() {
return hasClassName() ? className.getReference(line) : null;
}
@Override
public String getMethodName() {
return hasMethodName() ? getEntryInLine(methodName) : null;
}
@Override
public String getFileName() {
return hasFileName() ? getEntryInLine(sourceFile) : null;
}
@Override
public int getLineNumber() {
if (!hasLineNumber()) {
return -1;
}
try {
return Integer.parseInt(getEntryInLine(lineNumber));
} catch (NumberFormatException nfe) {
return -1;
}
}
@Override
public String getFieldName() {
return hasFieldName() ? getEntryInLine(fieldName) : null;
}
@Override
public String getFieldOrReturnType() {
return hasFieldOrReturnType() ? getEntryInLine(fieldOrReturnType) : null;
}
@Override
public String getMethodArguments() {
return hasMethodArguments() ? getEntryInLine(methodArguments) : null;
}
public String toRetracedItem(
RetraceStackTraceProxy<StackTraceElementStringProxy> retracedProxy,
boolean printAmbiguous,
boolean verbose) {
StringBuilder sb = new StringBuilder();
int lastSeenIndex = 0;
if (retracedProxy.isAmbiguous() && printAmbiguous) {
lastSeenIndex = firstNonWhiteSpaceCharacterFromIndex(line, 0);
sb.append(line, 0, lastSeenIndex);
sb.append("<OR> ");
}
for (StringIndex index : orderedIndices) {
sb.append(line, lastSeenIndex, index.startIndex);
sb.append(index.retracedString.apply(retracedProxy, this, verbose));
lastSeenIndex = index.endIndex;
}
sb.append(line, lastSeenIndex, line.length());
return sb.toString();
}
public String lineNumberAsString() {
return getEntryInLine(lineNumber);
}
private String getEntryInLine(StringIndex index) {
assert index != noIndex();
return line.substring(index.startIndex, index.endIndex);
}
public enum ClassNameType {
BINARY,
TYPENAME
}
public static class StackTraceElementStringProxyBuilder {
private final String line;
private final List<StringIndex> orderedIndices = new ArrayList<>();
private ClassStringIndex className = noIndex();
private StringIndex methodName = noIndex();
private StringIndex sourceFile = noIndex();
private StringIndex lineNumber = noIndex();
private StringIndex fieldName = noIndex();
private StringIndex fieldOrReturnType = noIndex();
private StringIndex methodArguments = noIndex();
private int lastSeenStartIndex = -1;
private StackTraceElementStringProxyBuilder(String line) {
this.line = line;
}
public StackTraceElementStringProxyBuilder registerClassName(
int startIndex, int endIndex, ClassNameType classNameType) {
ensureLineIndexIncreases(startIndex);
className =
new ClassStringIndex(
startIndex,
endIndex,
(retraced, original, verbose) -> {
assert retraced.hasRetracedClass();
RetracedClass retracedClass = retraced.getRetracedClass();
return classNameType == ClassNameType.BINARY
? retracedClass.getBinaryName()
: retracedClass.getTypeName();
},
classNameType);
orderedIndices.add(className);
return this;
}
public StackTraceElementStringProxyBuilder registerMethodName(int startIndex, int endIndex) {
methodName =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) -> {
if (!retraced.hasRetracedMethod()) {
return original.getMethodName();
}
return methodDescriptionFromRetraceMethod(
retraced.getRetracedMethod(), false, verbose);
});
orderedIndices.add(methodName);
return this;
}
public StackTraceElementStringProxyBuilder registerSourceFile(int startIndex, int endIndex) {
sourceFile =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) ->
retraced.hasSourceFile() ? retraced.getSourceFile() : original.getFileName());
orderedIndices.add(sourceFile);
return this;
}
public StackTraceElementStringProxyBuilder registerLineNumber(int startIndex, int endIndex) {
lineNumber =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) ->
retraced.hasLineNumber()
? retraced.getLineNumber() + ""
: original.lineNumberAsString());
orderedIndices.add(lineNumber);
return this;
}
public StackTraceElementStringProxyBuilder registerFieldName(int startIndex, int endIndex) {
fieldName =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) -> {
if (!retraced.hasRetracedField()) {
return original.getFieldName();
}
RetracedField retracedField = retraced.getRetracedField();
if (!verbose || retracedField.isUnknown()) {
return retracedField.getFieldName();
}
return retracedField.asKnown().getFieldType().getTypeName()
+ " "
+ retracedField.getFieldName();
});
orderedIndices.add(fieldName);
return this;
}
public StackTraceElementStringProxyBuilder registerFieldOrReturnType(
int startIndex, int endIndex) {
fieldOrReturnType =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) -> {
if (!retraced.hasFieldOrReturnType()) {
return original.getFieldOrReturnType();
}
return retraced.getRetracedFieldOrReturnType().isVoid()
? "void"
: retraced.getRetracedFieldOrReturnType().getTypeName();
});
orderedIndices.add(fieldOrReturnType);
return this;
}
public StackTraceElementStringProxyBuilder registerMethodArguments(
int startIndex, int endIndex) {
methodArguments =
new StringIndex(
startIndex,
endIndex,
(retraced, original, verbose) -> {
if (!retraced.hasMethodArguments()) {
return original.getMethodArguments();
}
return StringUtils.join(
retraced.getMethodArguments(), ",", BraceType.NONE, RetracedType::getTypeName);
});
orderedIndices.add(methodArguments);
return this;
}
public StackTraceElementStringProxy build() {
return new StackTraceElementStringProxy(
line,
orderedIndices,
className,
methodName,
sourceFile,
lineNumber,
fieldName,
fieldOrReturnType,
methodArguments);
}
private void ensureLineIndexIncreases(int newStartIndex) {
if (lastSeenStartIndex >= newStartIndex) {
throw new RuntimeException("Parsing has to be incremental in the order of characters.");
}
lastSeenStartIndex = newStartIndex;
}
}
static class StringIndex {
private static final ClassStringIndex NO_INDEX =
new ClassStringIndex(-1, -1, null, ClassNameType.TYPENAME);
static ClassStringIndex noIndex() {
return NO_INDEX;
}
protected final int startIndex;
protected final int endIndex;
private final TriFunction<
RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
retracedString;
private StringIndex(
int startIndex,
int endIndex,
TriFunction<
RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
retracedString) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.retracedString = retracedString;
}
boolean hasIndex() {
return this != NO_INDEX;
}
}
static final class ClassStringIndex extends StringIndex {
private final ClassNameType classNameType;
private ClassStringIndex(
int startIndex,
int endIndex,
TriFunction<
RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
retracedString,
ClassNameType classNameType) {
super(startIndex, endIndex, retracedString);
this.classNameType = classNameType;
}
ClassReference getReference(String line) {
String className = line.substring(startIndex, endIndex);
return classNameType == ClassNameType.BINARY
? Reference.classFromBinaryName(className)
: Reference.classFromTypeName(className);
}
}
}