blob: 35e05411358c749a59ebc1712b20c8aad408ddac [file] [log] [blame]
package com.android.tools.r8.graph;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class MethodCollection {
// Threshold between using an array and a map for the backing store.
// Compiling R8 plus library shows classes with up to 30 methods account for about 95% of classes.
private static final int ARRAY_BACKING_THRESHOLD = 30;
private final DexClass holder;
private MethodCollectionBacking backing;
private DexEncodedMethod cachedClassInitializer = DexEncodedMethod.SENTINEL;
public MethodCollection(
DexClass holder, DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
this.holder = holder;
if (directMethods.length + virtualMethods.length > ARRAY_BACKING_THRESHOLD) {
backing = new MethodMapBacking();
} else {
backing = new MethodArrayBacking();
}
backing.setDirectMethods(directMethods);
backing.setVirtualMethods(virtualMethods);
}
private void resetCaches() {
resetDirectMethodCaches();
resetVirtualMethodCaches();
}
private void resetDirectMethodCaches() {
resetClassInitializerCache();
}
private void resetVirtualMethodCaches() {
// Nothing to do.
}
public boolean hasMethods(Predicate<DexEncodedMethod> predicate) {
return getMethod(predicate) != null;
}
public boolean hasDirectMethods() {
return hasDirectMethods(alwaysTrue());
}
public boolean hasDirectMethods(Predicate<DexEncodedMethod> predicate) {
return backing.getDirectMethod(predicate) != null;
}
public boolean hasVirtualMethods() {
return hasVirtualMethods(alwaysTrue());
}
public boolean hasVirtualMethods(Predicate<DexEncodedMethod> predicate) {
return backing.getVirtualMethod(predicate) != null;
}
public int numberOfDirectMethods() {
return backing.numberOfDirectMethods();
}
public int numberOfVirtualMethods() {
return backing.numberOfVirtualMethods();
}
public int size() {
return backing.size();
}
public TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
return backing.traverse(fn);
}
public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
forEachMethodMatching(alwaysTrue(), consumer);
}
public void forEachMethodMatching(
Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) {
backing.forEachMethod(
method -> {
if (predicate.test(method)) {
consumer.accept(method);
}
});
}
public void forEachDirectMethod(Consumer<DexEncodedMethod> consumer) {
forEachDirectMethodMatching(alwaysTrue(), consumer);
}
public void forEachDirectMethodMatching(
Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) {
backing.forEachDirectMethod(
method -> {
if (predicate.test(method)) {
consumer.accept(method);
}
});
}
public void forEachVirtualMethod(Consumer<DexEncodedMethod> consumer) {
forEachVirtualMethodMatching(alwaysTrue(), consumer);
}
public void forEachVirtualMethodMatching(
Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) {
backing.forEachVirtualMethod(
method -> {
if (predicate.test(method)) {
consumer.accept(method);
}
});
}
public Iterable<DexEncodedMethod> methods() {
return backing.methods();
}
public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
return IterableUtils.filter(methods(), predicate);
}
public List<DexEncodedMethod> allMethodsSorted() {
List<DexEncodedMethod> sorted = new ArrayList<>(size());
forEachMethod(sorted::add);
sorted.sort((a, b) -> a.getReference().compareTo(b.getReference()));
return sorted;
}
public Iterable<DexEncodedMethod> directMethods() {
return backing.directMethods();
}
public Iterable<DexEncodedMethod> virtualMethods() {
return backing.virtualMethods();
}
public DexEncodedMethod getMethod(DexMethod method) {
return backing.getMethod(method);
}
public DexEncodedMethod getMethod(Predicate<DexEncodedMethod> predicate) {
DexEncodedMethod result = backing.getDirectMethod(predicate);
return result != null ? result : backing.getVirtualMethod(predicate);
}
public DexEncodedMethod getDirectMethod(DexMethod method) {
return backing.getDirectMethod(method);
}
public DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
return backing.getDirectMethod(predicate);
}
public DexEncodedMethod getVirtualMethod(DexMethod method) {
return backing.getVirtualMethod(method);
}
public DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
return backing.getVirtualMethod(predicate);
}
private void resetClassInitializerCache() {
cachedClassInitializer = DexEncodedMethod.SENTINEL;
}
public synchronized DexEncodedMethod getClassInitializer() {
if (cachedClassInitializer == DexEncodedMethod.SENTINEL) {
cachedClassInitializer = null;
for (DexEncodedMethod directMethod : directMethods()) {
if (directMethod.isClassInitializer()) {
cachedClassInitializer = directMethod;
break;
}
}
}
return cachedClassInitializer;
}
public void addMethod(DexEncodedMethod method) {
resetCaches();
backing.addMethod(method);
}
public void addVirtualMethod(DexEncodedMethod virtualMethod) {
resetVirtualMethodCaches();
backing.addVirtualMethod(virtualMethod);
}
public void addDirectMethod(DexEncodedMethod directMethod) {
resetDirectMethodCaches();
backing.addDirectMethod(directMethod);
}
public DexEncodedMethod replaceDirectMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetDirectMethodCaches();
return backing.replaceDirectMethod(method, replacement);
}
public DexEncodedMethod replaceVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetVirtualMethodCaches();
return backing.replaceVirtualMethod(method, replacement);
}
public void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetCaches();
backing.replaceMethods(replacement);
}
public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetDirectMethodCaches();
backing.replaceDirectMethods(replacement);
}
public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetVirtualMethodCaches();
backing.replaceVirtualMethods(replacement);
}
public void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetDirectMethodCaches();
backing.replaceAllDirectMethods(replacement);
}
public void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetVirtualMethodCaches();
backing.replaceAllVirtualMethods(replacement);
}
/**
* Replace a direct method, if found, by a computed virtual method using the replacement function.
*
* @param method Direct method to replace if present.
* @param replacement Replacement function computing the virtual replacement.
* @return Returns the replacement if found, null otherwise.
*/
public DexEncodedMethod replaceDirectMethodWithVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetCaches();
return backing.replaceDirectMethodWithVirtualMethod(method, replacement);
}
public void addDirectMethods(Collection<DexEncodedMethod> methods) {
assert verifyCorrectnessOfMethodHolders(methods);
resetDirectMethodCaches();
backing.addDirectMethods(methods);
}
public void clearDirectMethods() {
resetDirectMethodCaches();
backing.clearDirectMethods();
}
public DexEncodedMethod removeMethod(DexMethod method) {
DexEncodedMethod removed = backing.removeMethod(method);
if (removed != null) {
if (backing.belongsToDirectPool(removed)) {
resetDirectMethodCaches();
} else {
assert backing.belongsToVirtualPool(removed);
resetVirtualMethodCaches();
}
}
return removed;
}
public void removeMethods(Set<DexEncodedMethod> methods) {
backing.removeMethods(methods);
resetDirectMethodCaches();
resetVirtualMethodCaches();
}
public void setDirectMethods(DexEncodedMethod[] methods) {
assert verifyCorrectnessOfMethodHolders(methods);
resetDirectMethodCaches();
backing.setDirectMethods(methods);
}
public void setSingleDirectMethod(DexEncodedMethod method) {
setDirectMethods(new DexEncodedMethod[] {method});
}
public void addVirtualMethods(Collection<DexEncodedMethod> methods) {
assert verifyCorrectnessOfMethodHolders(methods);
resetVirtualMethodCaches();
backing.addVirtualMethods(methods);
}
public void clearVirtualMethods() {
resetVirtualMethodCaches();
backing.clearVirtualMethods();
}
public void setVirtualMethods(DexEncodedMethod[] methods) {
assert verifyCorrectnessOfMethodHolders(methods);
resetVirtualMethodCaches();
backing.setVirtualMethods(methods);
}
public void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
resetVirtualMethodCaches();
backing.virtualizeMethods(privateInstanceMethods);
}
public boolean hasAnnotations() {
return traverse(
method ->
method.hasAnnotation()
? TraversalContinuation.BREAK
: TraversalContinuation.CONTINUE)
.shouldBreak();
}
public void useSortedBacking() {
assert size() == 0;
backing = MethodMapBacking.createSorted();
}
public boolean verify() {
forEachMethod(
method -> {
assert verifyCorrectnessOfMethodHolder(method);
});
assert backing.verify();
return true;
}
private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
assert method.getHolderType() == holder.type
: "Expected method `"
+ method.getReference().toSourceString()
+ "` to have holder `"
+ holder.type.toSourceString()
+ "`";
return true;
}
private boolean verifyCorrectnessOfMethodHolders(DexEncodedMethod[] methods) {
if (methods == null) {
return true;
}
return verifyCorrectnessOfMethodHolders(Arrays.asList(methods));
}
private boolean verifyCorrectnessOfMethodHolders(Iterable<DexEncodedMethod> methods) {
for (DexEncodedMethod method : methods) {
assert verifyCorrectnessOfMethodHolder(method);
}
return true;
}
}