blob: ea4be31e8e83906f6d980de587b270153740f07a [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.graph;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class DexMethod extends DexMember<DexEncodedMethod, DexMethod> {
@SuppressWarnings("ReferenceEquality")
public static boolean identical(DexMethod t1, DexMethod t2) {
return t1 == t2;
}
public final boolean isIdenticalTo(DexMethod other) {
return identical(this, other);
}
public final boolean isNotIdenticalTo(DexMethod other) {
return !isIdenticalTo(other);
}
public final DexProto proto;
DexMethod(DexType holder, DexProto proto, DexString name, boolean skipNameValidationForTesting) {
super(holder, name);
this.proto = proto;
if (!skipNameValidationForTesting && !name.isValidMethodName()) {
throw new CompilationError(
"Method name '" + name + "' in class '" + holder.toSourceString() +
"' cannot be represented in dex format.");
}
}
private static void specify(StructuralSpecification<DexMethod, ?> spec) {
spec.withItem(DexMethod::getHolderType).withItem(DexMethod::getName).withItem(m -> m.proto);
}
@Override
public int compareTo(DexReference other) {
if (other.isDexMethod()) {
return compareTo(other.asDexMethod());
}
int comparisonResult = getHolderType().compareTo(other.getContextType());
return comparisonResult != 0 ? comparisonResult : 1;
}
@Override
public StructuralMapping<DexMethod> getStructuralMapping() {
return DexMethod::specify;
}
@Override
public DexMethod self() {
return this;
}
@Override
public int acceptCompareTo(DexMethod other, CompareToVisitor visitor) {
return visitor.visitDexMethod(this, other);
}
@Override
public void acceptHashing(HashingVisitor visitor) {
visitor.visitDexMethod(this);
}
@Override
public LirConstantOrder getLirConstantOrder() {
return LirConstantOrder.METHOD;
}
@Override
public int internalLirConstantAcceptCompareTo(LirConstant other, CompareToVisitor visitor) {
return acceptCompareTo((DexMethod) other, visitor);
}
@Override
public void internalLirConstantAcceptHashing(HashingVisitor visitor) {
acceptHashing(visitor);
}
public DexType getArgumentType(int argumentIndex, boolean isStatic) {
if (isStatic) {
return getParameter(argumentIndex);
}
if (argumentIndex == 0) {
return getHolderType();
}
return getParameter(argumentIndex - 1);
}
public DexType getArgumentTypeForNonStaticMethod(int argumentIndex) {
return getArgumentType(argumentIndex, false);
}
public int getNumberOfArguments(boolean isStatic) {
return getArity() + BooleanUtils.intValue(!isStatic);
}
public int getNumberOfArgumentsForNonStaticMethod() {
return getNumberOfArguments(false);
}
public DexType getParameter(int index) {
return proto.getParameter(index);
}
public DexTypeList getParameters() {
return proto.parameters;
}
public DexProto getProto() {
return proto;
}
public DexType getReturnType() {
return proto.returnType;
}
@Override
public <T> T apply(Function<DexField, T> fieldConsumer, Function<DexMethod, T> methodConsumer) {
return methodConsumer.apply(this);
}
@Override
public <T> T apply(
Function<DexType, T> classConsumer,
Function<DexField, T> fieldConsumer,
Function<DexMethod, T> methodConsumer) {
return methodConsumer.apply(this);
}
@Override
public void accept(
Consumer<DexType> classConsumer,
Consumer<DexField> fieldConsumer,
Consumer<DexMethod> methodConsumer) {
methodConsumer.accept(this);
}
@Override
public <T> void accept(
BiConsumer<DexType, T> classConsumer,
BiConsumer<DexField, T> fieldConsumer,
BiConsumer<DexMethod, T> methodConsumer,
T arg) {
methodConsumer.accept(this, arg);
}
@Override
public DexEncodedMethod lookupOnClass(DexClass clazz) {
return clazz != null ? clazz.lookupMember(this) : null;
}
@Override
public DexClassAndMethod lookupMemberOnClass(DexClass clazz) {
return clazz != null ? clazz.lookupClassMethod(this) : null;
}
@Override
public ProgramMethod lookupOnProgramClass(DexProgramClass clazz) {
return clazz != null ? clazz.lookupProgramMethod(this) : null;
}
@Override
public String toString() {
return toSourceString();
}
public MethodReference asMethodReference() {
List<TypeReference> parameters = new ArrayList<>();
for (DexType value : proto.parameters.values) {
parameters.add(Reference.typeFromDescriptor(value.toDescriptorString()));
}
String returnTypeDescriptor = proto.returnType.toDescriptorString();
TypeReference returnType =
returnTypeDescriptor.equals("V")
? null
: Reference.typeFromDescriptor(returnTypeDescriptor);
return Reference.method(
Reference.classFromDescriptor(holder.toDescriptorString()),
name.toString(),
parameters,
returnType);
}
public int getArity() {
return proto.parameters.size();
}
public DexMethodSignature getSignature() {
return DexMethodSignature.create(this);
}
@Override
public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
if (collectIndexedItemsExceptName(appView, indexedItems)) {
collectIndexedItemsName(appView, indexedItems);
}
}
boolean collectIndexedItemsExceptName(AppView<?> appView, IndexedItemCollection indexedItems) {
if (indexedItems.addMethod(this)) {
holder.collectIndexedItems(appView, indexedItems);
proto.collectIndexedItems(appView, indexedItems);
return true;
}
return false;
}
void collectIndexedItemsName(AppView<?> appView, IndexedItemCollection indexedItems) {
appView.getNamingLens().lookupName(this).collectIndexedItems(indexedItems);
}
@Override
public int getOffset(ObjectToOffsetMapping mapping) {
return mapping.getOffsetFor(this);
}
@Override
public boolean isDexMethod() {
return true;
}
@Override
public DexMethod asDexMethod() {
return this;
}
@Override
public Iterable<DexType> getReferencedTypes() {
return proto.getTypes();
}
@Override
public int computeHashCode() {
return holder.hashCode() * 7 + proto.hashCode() * 29 + name.hashCode() * 31;
}
@Override
public boolean computeEquals(Object other) {
if (other instanceof DexMethod) {
DexMethod o = (DexMethod) other;
return holder.equals(o.holder)
&& name.equals(o.name)
&& proto.equals(o.proto);
}
return false;
}
@Override
public boolean match(DexMethod method) {
return isIdenticalTo(method) || match(method.getProto(), method.getName());
}
public boolean match(DexMethodSignature method) {
return match(method.getProto(), method.getName());
}
public boolean match(DexProto methodProto, DexString methodName) {
return proto.isIdenticalTo(methodProto) && name.isIdenticalTo(methodName);
}
@Override
public boolean match(DexEncodedMethod encodedMethod) {
return match(encodedMethod.getReference());
}
public String qualifiedName() {
return holder + "." + name;
}
@Override
public String toSmaliString() {
return holder.toSmaliString() + "->" + name + proto.toSmaliString();
}
@Override
public String toSourceString() {
return toSourceString(true, true);
}
public String toSourceStringWithoutHolder() {
return toSourceString(false, true);
}
public String toSourceStringWithoutHolderAndReturnType() {
return toSourceString(false, false);
}
public String toSourceStringWithoutReturnType() {
return toSourceString(true, false);
}
private String toSourceString(boolean includeHolder, boolean includeReturnType) {
StringBuilder builder = new StringBuilder();
if (includeReturnType) {
builder.append(getReturnType().toSourceString()).append(" ");
}
if (includeHolder) {
builder.append(holder.toSourceString()).append(".");
}
builder.append(name).append("(");
for (int i = 0; i < getArity(); i++) {
if (i != 0) {
builder.append(", ");
}
builder.append(proto.parameters.values[i].toSourceString());
}
return builder.append(")").toString();
}
public boolean isLambdaDeserializeMethod(DexItemFactory dexItemFactory) {
return dexItemFactory.deserializeLambdaMethodName.isIdenticalTo(name)
&& dexItemFactory.deserializeLambdaMethodProto.isIdenticalTo(proto);
}
public boolean isInstanceInitializer(DexItemFactory factory) {
return factory.isConstructor(this);
}
public DexMethod withExtraArgumentPrepended(DexType type, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
holder, getProto().prependParameter(type, dexItemFactory), name);
}
public DexMethod withHolder(DexDefinition definition, DexItemFactory dexItemFactory) {
return withHolder(definition.getContextType(), dexItemFactory);
}
@Override
public DexMethod withHolder(DexReference reference, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(reference.getContextType(), proto, name);
}
public DexMethod withName(String name, DexItemFactory dexItemFactory) {
return withName(dexItemFactory.createString(name), dexItemFactory);
}
public DexMethod withName(DexString name, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(holder, proto, name);
}
public DexMethod withProto(DexProto proto, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(holder, proto, name);
}
}