blob: 0e838b9f116cdde4ef39b6183e84f4eed0399a0a [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.graph;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.function.Function;
import java.util.function.Predicate;
public class MethodMapBacking extends MethodCollectionBacking {
private final boolean isSorted;
private SortedMap<DexMethodSignature, DexEncodedMethod> methodMap;
@Override
String getDescriptionString() {
return "<method-mapbacking/" + (isSorted ? "sorted" : "linked") + ">";
}
private MethodMapBacking(
boolean isSorted, SortedMap<DexMethodSignature, DexEncodedMethod> methodMap) {
this.isSorted = isSorted;
this.methodMap = methodMap;
}
public static MethodMapBacking createSorted() {
return new MethodMapBacking(true, createSortedMap());
}
public static MethodMapBacking createLinked(int capacity) {
return new MethodMapBacking(false, createdLinkedMap(capacity));
}
// Internal create for reallocating.
private SortedMap<DexMethodSignature, DexEncodedMethod> createMap(int capacity) {
return isSorted ? createSortedMap() : createdLinkedMap(capacity);
}
private static SortedMap<DexMethodSignature, DexEncodedMethod> createSortedMap() {
return new Object2ObjectRBTreeMap<>();
}
private static SortedMap<DexMethodSignature, DexEncodedMethod> createdLinkedMap(int capacity) {
// Maintain a linked map so the output order remains a deterministic function of the input.
return new Object2ReferenceLinkedOpenHashMap<>(capacity);
}
private void replace(DexMethodSignature existingKey, DexEncodedMethod method) {
if (method.getReference().match(existingKey)) {
methodMap.put(existingKey, method);
} else {
methodMap.remove(existingKey);
methodMap.put(method.getSignature(), method);
}
}
@Override
boolean verify() {
methodMap.forEach(
(signature, method) -> {
assert method.getReference().match(signature);
});
return true;
}
@Override
public int numberOfDirectMethods() {
return numberOfMethodsMatching(this::belongsToDirectPool);
}
@Override
public int numberOfVirtualMethods() {
return numberOfMethodsMatching(this::belongsToVirtualPool);
}
private int numberOfMethodsMatching(Predicate<DexEncodedMethod> predicate) {
int count = 0;
for (DexEncodedMethod method : methodMap.values()) {
if (predicate.test(method)) {
count++;
}
}
return count;
}
@Override
int size() {
return methodMap.size();
}
@Override
TraversalContinuation<?, ?> traverse(Function<DexEncodedMethod, TraversalContinuation<?, ?>> fn) {
for (Entry<DexMethodSignature, DexEncodedMethod> entry : methodMap.entrySet()) {
TraversalContinuation<?, ?> result = fn.apply(entry.getValue());
if (result.shouldBreak()) {
return result;
}
}
return TraversalContinuation.doContinue();
}
@Override
Iterable<DexEncodedMethod> methods() {
return methodMap.values();
}
@Override
Iterable<DexEncodedMethod> directMethods() {
return () -> IteratorUtils.filter(methodMap.values().iterator(), this::belongsToDirectPool);
}
@Override
Iterable<DexEncodedMethod> virtualMethods() {
return () -> IteratorUtils.filter(methodMap.values().iterator(), this::belongsToVirtualPool);
}
@Override
DexEncodedMethod getMethod(DexProto methodProto, DexString methodName) {
return getMethod(DexMethodSignature.create(methodName, methodProto));
}
DexEncodedMethod getMethod(DexMethodSignature method) {
return methodMap.get(method);
}
private DexEncodedMethod getMethod(Predicate<DexEncodedMethod> predicate) {
Box<DexEncodedMethod> found = new Box<>();
traverse(
method -> {
if (predicate.test(method)) {
found.set(method);
return TraversalContinuation.doBreak();
}
return TraversalContinuation.doContinue();
});
return found.get();
}
@Override
DexEncodedMethod getDirectMethod(DexMethod method) {
DexEncodedMethod definition = getMethod(method.getProto(), method.getName());
return definition != null && belongsToDirectPool(definition) ? definition : null;
}
@Override
DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
Predicate<DexEncodedMethod> isDirect = this::belongsToDirectPool;
return getMethod(isDirect.and(predicate));
}
@Override
DexEncodedMethod getVirtualMethod(DexMethod method) {
DexEncodedMethod definition = getMethod(method.getProto(), method.getName());
return definition != null && belongsToVirtualPool(definition) ? definition : null;
}
@Override
DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
Predicate<DexEncodedMethod> isVirtual = this::belongsToVirtualPool;
return getMethod(isVirtual.and(predicate));
}
@Override
void addMethod(DexEncodedMethod method) {
DexMethodSignature key = method.getSignature();
DexEncodedMethod old = methodMap.put(key, method);
assert old == null;
}
@Override
void addDirectMethod(DexEncodedMethod method) {
assert belongsToDirectPool(method);
addMethod(method);
}
@Override
void addVirtualMethod(DexEncodedMethod method) {
assert belongsToVirtualPool(method);
addMethod(method);
}
@Override
void addDirectMethods(Collection<DexEncodedMethod> methods) {
for (DexEncodedMethod method : methods) {
addDirectMethod(method);
}
}
@Override
void addVirtualMethods(Collection<DexEncodedMethod> methods) {
for (DexEncodedMethod method : methods) {
addVirtualMethod(method);
}
}
@Override
void clearDirectMethods() {
methodMap.values().removeIf(this::belongsToDirectPool);
}
@Override
void clearVirtualMethods() {
methodMap.values().removeIf(this::belongsToVirtualPool);
}
@Override
DexEncodedMethod removeMethod(DexMethod method) {
return removeMethod(method.getSignature());
}
DexEncodedMethod removeMethod(DexMethodSignature method) {
return methodMap.remove(method);
}
@Override
void removeMethods(Set<DexEncodedMethod> methods) {
methods.forEach(method -> methodMap.remove(method.getSignature()));
}
@Override
void setDirectMethods(DexEncodedMethod[] methods) {
if ((methods == null || methods.length == 0) && methodMap.isEmpty()) {
return;
}
if (methods == null) {
methods = DexEncodedMethod.EMPTY_ARRAY;
}
SortedMap<DexMethodSignature, DexEncodedMethod> newMap = createMap(size() + methods.length);
forEachMethod(
method -> {
if (belongsToVirtualPool(method)) {
newMap.put(method.getSignature(), method);
}
});
for (DexEncodedMethod method : methods) {
assert belongsToDirectPool(method);
newMap.put(method.getSignature(), method);
}
methodMap = newMap;
}
@Override
void setVirtualMethods(DexEncodedMethod[] methods) {
if ((methods == null || methods.length == 0) && methodMap.isEmpty()) {
return;
}
if (methods == null) {
methods = DexEncodedMethod.EMPTY_ARRAY;
}
SortedMap<DexMethodSignature, DexEncodedMethod> newMap = createMap(size() + methods.length);
forEachMethod(
method -> {
if (belongsToDirectPool(method)) {
newMap.put(method.getSignature(), method);
}
});
for (DexEncodedMethod method : methods) {
assert belongsToVirtualPool(method);
newMap.put(method.getSignature(), method);
}
methodMap = newMap;
}
@Override
void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
// The code assumes that when replacement.apply(method) is called, the map is up-to-date with
// the previously replaced methods. We therefore cannot postpone the map updates to the end of
// the method.
ArrayList<DexEncodedMethod> initialValues = new ArrayList<>(methodMap.values());
for (DexEncodedMethod method : initialValues) {
DexEncodedMethod newMethod = replacement.apply(method);
if (newMethod != method) {
DexMethodSignature signature = method.getSignature();
if (getMethod(signature) == method) {
removeMethod(signature);
}
methodMap.put(newMethod.getSignature(), newMethod);
}
}
}
@Override
void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
replaceMethods(method -> belongsToDirectPool(method) ? replacement.apply(method) : method);
}
@Override
void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
replaceMethods(method -> belongsToVirtualPool(method) ? replacement.apply(method) : method);
}
@Override
void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
List<DexEncodedMethod> oldMethods = Lists.newArrayList(directMethods());
clearDirectMethods();
List<DexEncodedMethod> newMethods = new ArrayList<>(oldMethods.size());
for (DexEncodedMethod method : oldMethods) {
newMethods.add(replacement.apply(method));
}
addDirectMethods(newMethods);
}
@Override
void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
List<DexEncodedMethod> oldMethods = Lists.newArrayList(virtualMethods());
clearVirtualMethods();
List<DexEncodedMethod> newMethods = new ArrayList<>(oldMethods.size());
for (DexEncodedMethod method : oldMethods) {
newMethods.add(replacement.apply(method));
}
addVirtualMethods(newMethods);
}
@Override
DexEncodedMethod replaceDirectMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
return replaceMethod(method, replacement, this::belongsToDirectPool);
}
@Override
DexEncodedMethod replaceVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
return replaceMethod(method, replacement, this::belongsToVirtualPool);
}
private DexEncodedMethod replaceMethod(
DexMethod method,
Function<DexEncodedMethod, DexEncodedMethod> replacement,
Predicate<DexEncodedMethod> predicate) {
DexMethodSignature key = method.getSignature();
DexEncodedMethod existing = methodMap.get(key);
if (existing == null || !predicate.test(existing)) {
return null;
}
DexEncodedMethod newMethod = replacement.apply(existing);
assert predicate.test(newMethod);
replace(key, newMethod);
return newMethod;
}
@Override
DexEncodedMethod replaceDirectMethodWithVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
DexMethodSignature key = method.getSignature();
DexEncodedMethod existing = methodMap.get(key);
if (existing == null || belongsToVirtualPool(existing)) {
return null;
}
DexEncodedMethod newMethod = replacement.apply(existing);
assert belongsToVirtualPool(newMethod);
replace(key, newMethod);
return newMethod;
}
@Override
void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
// This is a no-op as the virtualizer has modified the encoded method bits.
assert verifyVirtualizedMethods(privateInstanceMethods);
}
private boolean verifyVirtualizedMethods(Set<DexEncodedMethod> methods) {
for (DexEncodedMethod method : methods) {
assert belongsToVirtualPool(method);
assert methodMap.get(method.getSignature()) == method;
}
return true;
}
@Override
MethodMapBacking map(Function<DexEncodedMethod, DexEncodedMethod> fn) {
MethodMapBacking newBacking = new MethodMapBacking(isSorted, createMap(methodMap.size()));
methodMap.forEach((ignore, method) -> newBacking.addMethod(fn.apply(method)));
return newBacking;
}
}