| // 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.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> { |
| |
| 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.visitDexType(holder); |
| visitor.visitDexString(name); |
| getReferencedTypes().forEach(visitor::visitDexType); |
| } |
| |
| public DexType getArgumentType(int argumentIndex, boolean isStatic) { |
| if (isStatic) { |
| return getParameter(argumentIndex); |
| } |
| if (argumentIndex == 0) { |
| return getHolderType(); |
| } |
| return getParameter(argumentIndex - 1); |
| } |
| |
| public int getNumberOfArguments(boolean isStatic) { |
| return getArity() + BooleanUtils.intValue(!isStatic); |
| } |
| |
| 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 "Method " + holder + "." + name + " " + proto.toString(); |
| } |
| |
| 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(IndexedItemCollection indexedItems) { |
| if (collectIndexedItemsExceptName(indexedItems)) { |
| collectIndexedItemsName(indexedItems); |
| } |
| } |
| |
| boolean collectIndexedItemsExceptName(IndexedItemCollection indexedItems) { |
| if (indexedItems.addMethod(this)) { |
| holder.collectIndexedItems(indexedItems); |
| proto.collectIndexedItems(indexedItems); |
| return true; |
| } |
| return false; |
| } |
| |
| void collectIndexedItemsName(IndexedItemCollection indexedItems) { |
| indexedItems.getRenamedName(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 method == this || 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 == methodProto && name == 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); |
| } |
| |
| 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 name == dexItemFactory.deserializeLambdaMethodName |
| && proto == dexItemFactory.deserializeLambdaMethodProto; |
| } |
| |
| public boolean isInstanceInitializer(DexItemFactory factory) { |
| return factory.isConstructor(this); |
| } |
| |
| public boolean mustBeInlinedIntoInstanceInitializer(DexItemFactory dexItemFactory) { |
| return getName().startsWith(dexItemFactory.temporaryConstructorMethodPrefix); |
| } |
| |
| public DexMethod withExtraArgumentPrepended(DexType type, DexItemFactory dexItemFactory) { |
| return dexItemFactory.createMethod( |
| holder, dexItemFactory.prependTypeToProto(type, proto), name); |
| } |
| |
| public DexMethod withHolder(DexDefinition definition, DexItemFactory dexItemFactory) { |
| return withHolder(definition.getContextType(), dexItemFactory); |
| } |
| |
| @Override |
| public DexMethod withHolder(DexType 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); |
| } |
| } |