blob: de97bafe4a203464386285b932140164235b7343 [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.naming.NamingLens;
import com.google.common.collect.Maps;
import java.util.Map;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import java.util.ArrayList;
import java.util.List;
public class DexMethod extends Descriptor<DexEncodedMethod, DexMethod>
implements PresortedComparable<DexMethod> {
public final DexType holder;
public final DexProto proto;
public final DexString name;
// Caches used during processing.
private Map<DexType, DexEncodedMethod> singleTargetCache;
DexMethod(DexType holder, DexProto proto, DexString name, boolean skipNameValidationForTesting) {
this.holder = holder;
this.proto = proto;
this.name = name;
if (!skipNameValidationForTesting && !name.isValidMethodName()) {
throw new CompilationError(
"Method name '" + name + "' in class '" + holder.toSourceString() +
"' cannot be represented in dex format.");
}
}
@Override
public String toString() {
return "Method " + holder + "." + name + " " + proto.toString();
}
public MethodReference asMethodReference(AppView<?> appView) {
List<TypeReference> parameters = new ArrayList<>();
for (DexType value : proto.parameters.values) {
parameters.add(Reference.typeFromDescriptor(value.toDescriptorString()));
}
TypeReference returnType =
proto.returnType == appView.dexItemFactory().voidType
? null
: Reference.typeFromDescriptor(proto.returnType.toDescriptorString());
return Reference.method(
Reference.classFromDescriptor(holder.toDescriptorString()),
name.toString(),
parameters,
returnType);
}
public int getArity() {
return proto.parameters.size();
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
if (collectIndexedItemsExceptName(indexedItems, method, instructionOffset)) {
collectIndexedItemsName(indexedItems, method, instructionOffset);
}
}
public boolean collectIndexedItemsExceptName(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
if (indexedItems.addMethod(this)) {
holder.collectIndexedItems(indexedItems, method, instructionOffset);
proto.collectIndexedItems(indexedItems, method, instructionOffset);
return true;
}
return false;
}
public void collectIndexedItemsName(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
indexedItems.getRenamedName(this).collectIndexedItems(indexedItems, method, instructionOffset);
}
@Override
public int getOffset(ObjectToOffsetMapping mapping) {
return mapping.getOffsetFor(this);
}
@Override
public boolean isDexMethod() {
return true;
}
@Override
public DexMethod asDexMethod() {
return this;
}
@Override
public int computeHashCode() {
return holder.hashCode()
+ proto.hashCode() * 7
+ 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;
}
/**
* Returns true if the other method has the same name and prototype (including signature and
* return type), false otherwise.
*/
public boolean hasSameProtoAndName(DexMethod other) {
return name == other.name && proto == other.proto;
}
@Override
public int compareTo(DexMethod other) {
return sortedCompareTo(other.getSortedIndex());
}
@Override
public int slowCompareTo(DexMethod other) {
int result = holder.slowCompareTo(other.holder);
if (result != 0) {
return result;
}
result = name.slowCompareTo(other.name);
if (result != 0) {
return result;
}
return proto.slowCompareTo(other.proto);
}
@Override
public int slowCompareTo(DexMethod other, NamingLens namingLens) {
int result = holder.slowCompareTo(other.holder, namingLens);
if (result != 0) {
return result;
}
result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
if (result != 0) {
return result;
}
return proto.slowCompareTo(other.proto, namingLens);
}
@Override
public int layeredCompareTo(DexMethod other, NamingLens namingLens) {
int result = holder.compareTo(other.holder);
if (result != 0) {
return result;
}
result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
if (result != 0) {
return result;
}
return proto.compareTo(other.proto);
}
@Override
public boolean match(DexMethod method) {
return method.name == name && method.proto == proto;
}
@Override
public boolean match(DexEncodedMethod encodedMethod) {
return match(encodedMethod.method);
}
public String qualifiedName() {
return holder + "." + name;
}
@Override
public String toSmaliString() {
return holder.toSmaliString() + "->" + name + proto.toSmaliString();
}
@Override
public String toSourceString() {
return toSourceString(true);
}
public String toSourceStringWithoutHolder() {
return toSourceString(false);
}
private String toSourceString(boolean includeHolder) {
StringBuilder builder = new StringBuilder(proto.returnType.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;
}
synchronized public void setSingleVirtualMethodCache(
DexType receiverType, DexEncodedMethod method) {
if (singleTargetCache == null) {
singleTargetCache = Maps.newIdentityHashMap();
}
singleTargetCache.put(receiverType, method);
}
synchronized public boolean isSingleVirtualMethodCached(DexType receiverType) {
return singleTargetCache != null && singleTargetCache.containsKey(receiverType);
}
synchronized public DexEncodedMethod getSingleVirtualMethodCache(DexType receiverType) {
assert isSingleVirtualMethodCached(receiverType);
return singleTargetCache.get(receiverType);
}
}