blob: 51c3272ef06eeb97b57af8a8582a49f3e9b7a79e [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 static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import org.objectweb.asm.Type;
/**
* Stores renaming information for a member.
*
* <p>This includes the signature and the original name.
*/
public class MemberNaming {
@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);
}
@Override
public int hashCode() {
int result = signature.hashCode();
result = 31 * result + renamedSignature.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;
/** Position of the member in the file. */
final Position position;
public MemberNaming(Signature signature, String renamedName) {
this(signature, signature.asRenamed(renamedName), Position.UNKNOWN);
}
public MemberNaming(Signature signature, Signature renamedSignature, Position position) {
this.signature = signature;
this.renamedSignature = renamedSignature;
this.position = position;
}
public Signature getOriginalSignature() {
return signature;
}
public String getOriginalName() {
return signature.name;
}
public Signature getRenamedSignature() {
return renamedSignature;
}
public String getRenamedName() {
return renamedSignature.name;
}
public boolean isMethodNaming() {
return signature.kind() == SignatureKind.METHOD;
}
public boolean isFieldNaming() {
return signature.kind() == SignatureKind.FIELD;
}
public Position getPosition() {
return position;
}
@Override
public String toString() {
return signature.toString() + " -> " + renamedSignature.name;
}
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;
public boolean isQualified() {
return name.indexOf(JAVA_PACKAGE_SEPARATOR) != -1;
}
public String toUnqualifiedName() {
assert isQualified();
return name.substring(name.lastIndexOf(JAVA_PACKAGE_SEPARATOR) + 1);
}
public String toHolderFromQualified() {
assert isQualified();
return name.substring(0, name.lastIndexOf(JAVA_PACKAGE_SEPARATOR));
}
public boolean isMethodSignature() {
return false;
}
public boolean isFieldSignature() {
return false;
}
public MethodSignature asMethodSignature() {
return null;
}
public FieldSignature asFieldSignature() {
return null;
}
@Override
public String toString() {
try {
StringWriter writer = new StringWriter();
write(writer);
return writer.toString();
} catch (IOException e) {
// StringWriter is not throwing IOException
throw new Unreachable(e);
}
}
enum SignatureKind {
METHOD,
FIELD
}
}
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;
public FieldSignature(String name, String type) {
super(name);
this.type = type;
}
public static FieldSignature fromDexField(DexField field) {
return fromDexField(field, false);
}
public static FieldSignature fromDexField(DexField field, boolean withQualifiedName) {
return new FieldSignature(
withQualifiedName ? field.qualifiedName() : field.name.toSourceString(),
field.type.toSourceString());
}
public DexField toDexField(DexItemFactory factory, DexType clazz) {
return factory.createField(
clazz,
factory.createType(javaTypeToDescriptor(type)),
factory.createString(name));
}
@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);
}
@Override
public boolean isFieldSignature() {
return true;
}
@Override
public FieldSignature asFieldSignature() {
return this;
}
}
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 MethodSignature(String name, String type, Collection<String> parameters) {
super(name);
this.type = type;
this.parameters = parameters.toArray(StringUtils.EMPTY_ARRAY);
}
public static MethodSignature fromDexMethod(DexMethod method) {
return fromDexMethod(method, false);
}
public static MethodSignature fromDexMethod(DexMethod method, boolean withQualifiedName) {
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(
withQualifiedName ? method.qualifiedName() : method.name.toSourceString(),
method.proto.returnType.toSourceString(),
paramNames);
}
public static MethodSignature fromSignature(String name, String signature) {
Type[] parameterDescriptors = Type.getArgumentTypes(signature);
Type returnDescriptor = Type.getReturnType(signature);
String[] parameterTypes = new String[parameterDescriptors.length];
for (int i = 0; i < parameterDescriptors.length; i++) {
parameterTypes[i] =
DescriptorUtils.descriptorToJavaType(parameterDescriptors[i].getDescriptor());
}
return new MethodSignature(
name,
DescriptorUtils.descriptorToJavaType(returnDescriptor.getDescriptor()),
parameterTypes);
}
public MethodSignature toUnqualified() {
assert isQualified();
return new MethodSignature(toUnqualifiedName(), type, parameters);
}
public DexMethod toDexMethod(DexItemFactory factory, DexType clazz) {
DexType[] paramTypes = new DexType[parameters.length];
for (int i = 0; i < parameters.length; i++) {
paramTypes[i] = factory.createType(javaTypeToDescriptor(parameters[i]));
}
DexType returnType = factory.createType(javaTypeToDescriptor(type));
return factory.createMethod(
clazz,
factory.createProto(returnType, paramTypes),
factory.createString(name));
}
public static MethodSignature initializer(String[] parameters) {
return new MethodSignature(Constants.INSTANCE_INITIALIZER_NAME, "void", parameters);
}
@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
public String toString() {
return type + ' ' + name + '(' + String.join(",", 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 String toDescriptor() {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (String parameterType : parameters) {
sb.append(javaTypeToDescriptor(parameterType));
}
sb.append(')');
sb.append(javaTypeToDescriptor(type));
return sb.toString();
}
@Override
public boolean isMethodSignature() {
return true;
}
@Override
public MethodSignature asMethodSignature() {
return this;
}
}
}