blob: 558ea43d40660223e893ebae140f6b2ecf23da62 [file] [log] [blame]
// Copyright (c) 2020, 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.utils;
import static com.android.tools.r8.utils.ClassReferenceUtils.getClassReferenceComparator;
import static com.android.tools.r8.utils.TypeReferenceUtils.getTypeReferenceComparator;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.references.ArrayReference;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class MethodReferenceUtils {
private static final Comparator<MethodReference> COMPARATOR =
(method, other) -> {
CompareResult holderClassCompareResult =
CompareResult.compare(
method.getHolderClass(), other.getHolderClass(), getClassReferenceComparator());
if (!holderClassCompareResult.isEqual()) {
return holderClassCompareResult.getComparisonResult();
}
CompareResult methodNameCompareResult =
CompareResult.compare(method.getMethodName(), other.getMethodName());
if (!methodNameCompareResult.isEqual()) {
return methodNameCompareResult.getComparisonResult();
}
CompareResult returnTypeCompareResult =
CompareResult.compare(
method.getReturnType(), other.getReturnType(), getTypeReferenceComparator());
if (!returnTypeCompareResult.isEqual()) {
return returnTypeCompareResult.getComparisonResult();
}
for (int i = 0;
i < Math.min(method.getFormalTypes().size(), other.getFormalTypes().size());
i++) {
CompareResult formalTypeCompareResult =
CompareResult.compare(
method.getFormalTypes().get(i),
other.getFormalTypes().get(i),
getTypeReferenceComparator());
if (!formalTypeCompareResult.isEqual()) {
return formalTypeCompareResult.getComparisonResult();
}
}
return method.getFormalTypes().size() - other.getFormalTypes().size();
};
public static MethodReference classConstructor(Class<?> clazz) {
return classConstructor(Reference.classFromClass(clazz));
}
public static MethodReference classConstructor(ClassReference type) {
return Reference.classConstructor(type);
}
public static MethodReference instanceConstructor(Class<?> clazz) {
return instanceConstructor(Reference.classFromClass(clazz));
}
public static MethodReference instanceConstructor(ClassReference type) {
return Reference.method(type, "<init>", Collections.emptyList(), null);
}
public static MethodReference instanceConstructor(
ClassReference type, TypeReference... formalTypes) {
return Reference.method(type, "<init>", Arrays.asList(formalTypes), null);
}
public static int compare(MethodReference methodReference, ClassReference other) {
return ClassReferenceUtils.compare(other, methodReference) * -1;
}
public static int compare(MethodReference methodReference, FieldReference other) {
return FieldReferenceUtils.compare(other, methodReference) * -1;
}
public static int compare(MethodReference methodReference, MethodReference other) {
return getMethodReferenceComparator().compare(methodReference, other);
}
public static Comparator<MethodReference> getMethodReferenceComparator() {
return COMPARATOR;
}
public static MethodReference mainMethod(Class<?> clazz) {
return mainMethod(Reference.classFromClass(clazz));
}
public static MethodReference mainMethod(ClassReference type) {
ArrayReference stringArrayType = Reference.array(Reference.classFromClass(String.class), 1);
return Reference.method(type, "main", ImmutableList.of(stringArrayType), null);
}
public static MethodReference methodFromMethod(
Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
return Reference.methodFromMethod(clazz.getDeclaredMethod(name, parameterTypes));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public static MethodReference parseSmaliString(String classAndMethodDescriptor) {
int arrowStartIndex = classAndMethodDescriptor.indexOf("->");
if (arrowStartIndex >= 0) {
return parseSmaliString(classAndMethodDescriptor, arrowStartIndex);
}
return null;
}
public static MethodReference parseSmaliString(
String classAndMethodDescriptor, int arrowStartIndex) {
String classDescriptor = classAndMethodDescriptor.substring(0, arrowStartIndex);
ClassReference methodHolder = ClassReferenceUtils.parseClassDescriptor(classDescriptor);
if (methodHolder == null) {
return null;
}
int methodNameStartIndex = arrowStartIndex + 2;
String protoWithNameDescriptor = classAndMethodDescriptor.substring(methodNameStartIndex);
int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
if (methodNameEndIndex <= 0) {
return null;
}
String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
return parseMethodProto(methodHolder, methodName, protoDescriptor);
}
private static MethodReference parseMethodProto(
ClassReference methodHolder, String methodName, String protoDescriptor) {
List<TypeReference> parameterTypes = new ArrayList<>();
for (String parameterTypeDescriptor :
DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
parameterTypes.add(Reference.typeFromDescriptor(parameterTypeDescriptor));
}
String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
TypeReference returnType = Reference.returnTypeFromDescriptor(returnTypeDescriptor);
return Reference.method(methodHolder, methodName, parameterTypes, returnType);
}
public static DexMethod toDexMethod(
MethodReference methodReference, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
ClassReferenceUtils.toDexType(methodReference.getHolderClass(), dexItemFactory),
TypeReferenceUtils.toDexProto(
methodReference.getFormalTypes(), methodReference.getReturnType(), dexItemFactory),
methodReference.getMethodName());
}
public static String toSmaliString(MethodReference methodReference) {
return methodReference.getHolderClass().getDescriptor()
+ "->"
+ methodReference.getMethodName()
+ methodReference.getMethodDescriptor();
}
public static String toSourceStringWithoutHolderAndReturnType(MethodReference methodReference) {
return toSourceString(methodReference, false, false);
}
public static String toSourceString(MethodReference methodReference) {
return toSourceString(methodReference, true, true);
}
public static String toSourceString(
MethodReference methodReference, boolean includeHolder, boolean includeReturnType) {
StringBuilder builder = new StringBuilder();
if (includeReturnType) {
builder
.append(
methodReference.getReturnType() != null
? methodReference.getReturnType().getTypeName()
: "void")
.append(" ");
}
if (includeHolder) {
builder.append(methodReference.getHolderClass().getTypeName()).append(".");
}
builder.append(methodReference.getMethodName()).append("(");
Iterator<TypeReference> formalTypesIterator = methodReference.getFormalTypes().iterator();
if (formalTypesIterator.hasNext()) {
builder.append(formalTypesIterator.next().getTypeName());
while (formalTypesIterator.hasNext()) {
builder.append(", ").append(formalTypesIterator.next().getTypeName());
}
}
return builder.append(")").toString();
}
public static MethodReference methodFromSmali(String smali) {
int holderEndIndex = smali.indexOf(";") + 1;
int methodDescriptorIndex = smali.indexOf("(", holderEndIndex);
return Reference.methodFromDescriptor(
smali.substring(0, holderEndIndex),
smali.substring(holderEndIndex, methodDescriptorIndex),
smali.substring(methodDescriptorIndex));
}
}