blob: 1537f055dfaae8d1de99a96437079dec754d9a86 [file] [log] [blame]
// Copyright (c) 2016, 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;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* Stores renaming information for a member.
* <p>
* This includes the signature, the original name and inlining range information.
*/
public class MemberNaming {
private static final int UNDEFINED_START_NUMBER = -1;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MemberNaming)) {
return false;
}
MemberNaming that = (MemberNaming) o;
return signature.equals(that.signature)
&& renamedSignature.equals(that.renamedSignature)
&& topLevelRange.equals(that.topLevelRange)
&& inlineInformation.equals(that.inlineInformation);
}
@Override
public int hashCode() {
int result = signature.hashCode();
result = 31 * result + renamedSignature.hashCode();
result = 31 * result + inlineInformation.hashCode();
result = 31 * result + topLevelRange.hashCode();
return result;
}
/**
* Original signature of the member
*/
final Signature signature;
/**
* Renamed signature where the name (but not the types) have been renamed.
*/
final Signature renamedSignature;
public final List<InlineInformation> inlineInformation = new LinkedList<>();
public final Range topLevelRange;
private int collapsedStartLineNumber = UNDEFINED_START_NUMBER;
private int originalStartLineNumber = UNDEFINED_START_NUMBER;
MemberNaming(Signature signature, String renamedName, Range inlinedLineRange) {
this.signature = signature;
this.renamedSignature = signature.asRenamed(renamedName);
topLevelRange = inlinedLineRange == null ? fakeZeroRange : inlinedLineRange;
}
public void addInliningRange(Range inlinedRange, Signature signature, Range originalRange) {
inlineInformation.add(new InlineInformation(inlinedRange, originalRange, signature));
}
public List<Range> getInlineRanges() {
List<Range> inlineRanges = new ArrayList<>();
for (InlineInformation information : inlineInformation) {
if (information.isActualInlining()) {
inlineRanges.add(information.inlinedRange);
}
}
return inlineRanges;
}
public Signature getOriginalSignature() {
return signature;
}
public String getRenamedName() {
return renamedSignature.name;
}
public void setCollapsedStartLineNumber(int value) {
assert collapsedStartLineNumber == UNDEFINED_START_NUMBER;
collapsedStartLineNumber = value;
}
public boolean isMethodNaming() {
return signature.kind() == SignatureKind.METHOD;
}
private int getCollapsedStartLineNumber() {
return collapsedStartLineNumber;
}
protected void write(Writer writer, boolean collapseRanges, boolean indent) throws IOException {
if (indent) {
writer.append(" ");
}
int rangeCounter =
collapseRanges ? getCollapsedStartLineNumber() : InlineInformation.DO_NOT_COLLAPSE;
// Avoid printing the range information if there was none in the original file.
if (topLevelRange != fakeZeroRange || rangeCounter != UNDEFINED_START_NUMBER) {
if (collapseRanges) {
// Skip ranges for methods that are used only once, as they do not have debug information.
if (rangeCounter != UNDEFINED_START_NUMBER) {
String rangeString = Integer.toString(rangeCounter);
writer.append(rangeString).append(":").append(rangeString).append(":");
} else {
rangeCounter = 0;
}
} else {
writer.append(topLevelRange.toString());
writer.append(':');
}
} else {
// We might end up in a case where we have no line information for the top entry but still
// have inline ranges. Just to be sure, set rangeCounter to a useful value.
if (collapseRanges) {
rangeCounter = 0;
}
}
signature.write(writer);
if (originalStartLineNumber != UNDEFINED_START_NUMBER) {
// If we have original line number information, print it here.
String originalSourceLineString = Integer.toString(originalStartLineNumber);
writer.append(':')
.append(originalSourceLineString)
.append(':')
.append(originalSourceLineString);
}
writer.append(" -> ");
writer.append(renamedSignature.name);
writer.append("\n");
for (InlineInformation information : inlineInformation) {
assert !collapseRanges || rangeCounter >= 0;
if (collapseRanges && information.isActualInlining()) {
rangeCounter++;
}
information.write(writer, rangeCounter, indent);
}
}
@Override
public String toString() {
try {
StringWriter writer = new StringWriter();
write(writer, false, false);
return writer.toString();
} catch (IOException e) {
return e.toString();
}
}
public void setOriginalStartLineNumber(int originalStartLineNumber) {
assert this.originalStartLineNumber == UNDEFINED_START_NUMBER;
this.originalStartLineNumber = originalStartLineNumber;
}
public abstract static class Signature {
public final String name;
protected Signature(String name) {
this.name = name;
}
abstract Signature asRenamed(String renamedName);
abstract public SignatureKind kind();
@Override
abstract public boolean equals(Object o);
@Override
abstract public int hashCode();
abstract void write(Writer builder) throws IOException;
@Override
public String toString() {
try {
StringWriter writer = new StringWriter();
write(writer);
return writer.toString();
} catch (IOException e) {
return e.toString();
}
}
enum SignatureKind {
METHOD,
FIELD
}
}
public static class FieldSignature extends Signature {
public final String type;
public FieldSignature(String name, String type) {
super(name);
this.type = type;
}
public static FieldSignature fromDexField(DexField field) {
return new FieldSignature(field.name.toSourceString(),
field.type.toSourceString());
}
@Override
Signature asRenamed(String renamedName) {
return new FieldSignature(renamedName, type);
}
@Override
public SignatureKind kind() {
return SignatureKind.FIELD;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof FieldSignature)) {
return false;
}
FieldSignature that = (FieldSignature) o;
return name.equals(that.name) && type.equals(that.type);
}
@Override
public int hashCode() {
return name.hashCode() * 31 + type.hashCode();
}
@Override
public String toString() {
return type + " " + name;
}
@Override
void write(Writer writer) throws IOException {
writer.append(type);
writer.append(' ');
writer.append(name);
}
}
public static class MethodSignature extends Signature {
public final String type;
public final String[] parameters;
public MethodSignature(String name, String type, String[] parameters) {
super(name);
this.type = type;
this.parameters = parameters;
}
public static MethodSignature fromDexMethod(DexMethod method) {
String[] paramNames = new String[method.getArity()];
DexType[] values = method.proto.parameters.values;
for (int i = 0; i < values.length; i++) {
paramNames[i] = values[i].toSourceString();
}
return new MethodSignature(method.name.toSourceString(),
method.proto.returnType.toSourceString(), paramNames);
}
@Override
Signature asRenamed(String renamedName) {
return new MethodSignature(renamedName, type, parameters);
}
@Override
public SignatureKind kind() {
return SignatureKind.METHOD;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MethodSignature)) {
return false;
}
MethodSignature that = (MethodSignature) o;
return type.equals(that.type)
&& name.equals(that.name)
&& Arrays.equals(parameters, that.parameters);
}
@Override
public int hashCode() {
return (type.hashCode() * 17
+ name.hashCode()) * 31
+ Arrays.hashCode(parameters);
}
@Override
void write(Writer writer) throws IOException {
writer.append(type)
.append(' ')
.append(name)
.append('(');
for (int i = 0; i < parameters.length; i++) {
writer.append(parameters[i]);
if (i < parameters.length - 1) {
writer.append(',');
}
}
writer.append(')');
}
}
public class InlineInformation {
static final int DO_NOT_COLLAPSE = -1;
public final Range inlinedRange;
public final Range originalRange;
public final Signature signature;
public InlineInformation(Range inlinedRange, Range originalRange, Signature signature) {
this.inlinedRange = inlinedRange;
this.originalRange = originalRange;
this.signature = signature;
}
public boolean isActualInlining() {
return !(originalRange instanceof SingleLineRange);
}
public void write(Writer writer, int collapsedRange, boolean indent) throws IOException {
if (indent) {
writer.append(" ");
}
if (collapsedRange == DO_NOT_COLLAPSE) {
writer.append(inlinedRange.toString());
} else {
writer.append(Range.toCollapsedString(collapsedRange));
}
writer.append(":");
signature.write(writer);
if (originalRange != null) {
writer.append(':')
.append(originalRange.toString());
}
writer.append(" -> ")
.append(renamedSignature.name);
writer.append("\n");
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof InlineInformation)) {
return false;
}
InlineInformation that = (InlineInformation) o;
return inlinedRange.equals(that.inlinedRange)
&& ((originalRange == null && that.originalRange == null)
|| originalRange.equals(that.originalRange))
&& signature.equals(that.signature);
}
@Override
public int hashCode() {
int result = inlinedRange.hashCode();
result = 31 * result + originalRange.hashCode();
result = 31 * result + signature.hashCode();
return result;
}
}
/**
* Represents a linenumber range.
*/
public static class Range {
public final int from;
public final int to;
Range(int from, int to) {
this.from = from;
this.to = to;
}
public boolean contains(int value) {
return value >= from && value <= to;
}
public boolean isSingle() {
return false;
}
@Override
public String toString() {
return from + ":" + to;
}
public static String toCollapsedString(int value) {
return value + ":" + value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Range)) {
return false;
}
Range range = (Range) o;
return from == range.from && to == range.to;
}
@Override
public int hashCode() {
int result = from;
result = 31 * result + to;
return result;
}
}
/**
* Represents a single linenumber range (':' followed by a signle number), which
* is different semantically from a normal range that has the same from and to.
*/
public static class SingleLineRange extends Range {
public SingleLineRange(int fromAndTo) {
super(fromAndTo, fromAndTo);
}
@Override
public boolean isSingle() {
return true;
}
@Override
public String toString() {
return Integer.toString(from);
}
}
public final static Range fakeZeroRange = new Range(0, 0);
}