blob: 7e67c3bfb1d669eea57983d0e8233c8f59e1a176 [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.ArrayUtils;
import com.android.tools.r8.utils.PredicateUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
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 MethodArrayBacking extends MethodCollectionBacking {
private DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
private DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
private boolean verifyNoDuplicateMethods() {
Set<DexMethod> unique = Sets.newIdentityHashSet();
forEachMethod(
method -> {
boolean changed = unique.add(method.getReference());
assert changed : "Duplicate method `" + method.getReference().toSourceString() + "`";
});
return true;
}
@Override
boolean verify() {
assert verifyNoDuplicateMethods();
return true;
}
@Override
public int numberOfDirectMethods() {
return directMethods.length;
}
@Override
public int numberOfVirtualMethods() {
return virtualMethods.length;
}
@Override
int size() {
return directMethods.length + virtualMethods.length;
}
@Override
TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
for (DexEncodedMethod method : directMethods) {
TraversalContinuation stepResult = fn.apply(method);
if (stepResult.shouldBreak()) {
return stepResult;
}
}
for (DexEncodedMethod method : virtualMethods) {
TraversalContinuation stepResult = fn.apply(method);
if (stepResult.shouldBreak()) {
return stepResult;
}
}
return TraversalContinuation.CONTINUE;
}
@Override
public Iterable<DexEncodedMethod> methods() {
return Iterables.concat(Arrays.asList(directMethods), Arrays.asList(virtualMethods));
}
@Override
List<DexEncodedMethod> directMethods() {
assert directMethods != null;
return Arrays.asList(directMethods);
}
@Override
void addDirectMethods(Collection<DexEncodedMethod> methods) {
DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + methods.size()];
System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
int i = directMethods.length;
for (DexEncodedMethod method : methods) {
newMethods[i] = method;
i++;
}
directMethods = newMethods;
assert verifyNoDuplicateMethods();
}
@Override
void clearDirectMethods() {
directMethods = DexEncodedMethod.EMPTY_ARRAY;
}
@Override
DexEncodedMethod removeMethod(DexMethod method) {
DexEncodedMethod removedDirectMethod =
removeMethodHelper(
method, directMethods, newDirectMethods -> directMethods = newDirectMethods);
if (removedDirectMethod != null) {
assert belongsToDirectPool(removedDirectMethod);
return removedDirectMethod;
}
DexEncodedMethod removedVirtualMethod =
removeMethodHelper(
method, virtualMethods, newVirtualMethods -> virtualMethods = newVirtualMethods);
assert removedVirtualMethod == null || belongsToVirtualPool(removedVirtualMethod);
return removedVirtualMethod;
}
private DexEncodedMethod removeMethodHelper(
DexMethod method,
DexEncodedMethod[] methods,
Consumer<DexEncodedMethod[]> newMethodsConsumer) {
for (int i = 0; i < methods.length; i++) {
if (method.match(methods[i])) {
return removeMethodWithIndex(i, methods, newMethodsConsumer);
}
}
return null;
}
@Override
void removeMethods(Set<DexEncodedMethod> methods) {
directMethods = removeMethodsHelper(methods, directMethods);
virtualMethods = removeMethodsHelper(methods, virtualMethods);
}
private static DexEncodedMethod[] removeMethodsHelper(
Set<DexEncodedMethod> methodsToRemove, DexEncodedMethod[] existingMethods) {
List<DexEncodedMethod> newMethods = new ArrayList<>(existingMethods.length);
for (DexEncodedMethod method : existingMethods) {
if (!methodsToRemove.contains(method)) {
newMethods.add(method);
}
}
return newMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
}
private DexEncodedMethod removeMethodWithIndex(
int index, DexEncodedMethod[] methods, Consumer<DexEncodedMethod[]> newMethodsConsumer) {
DexEncodedMethod removed = methods[index];
DexEncodedMethod[] newMethods = new DexEncodedMethod[methods.length - 1];
System.arraycopy(methods, 0, newMethods, 0, index);
System.arraycopy(methods, index + 1, newMethods, index, methods.length - index - 1);
newMethodsConsumer.accept(newMethods);
return removed;
}
@Override
void setDirectMethods(DexEncodedMethod[] methods) {
directMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
assert verifyNoDuplicateMethods();
}
@Override
List<DexEncodedMethod> virtualMethods() {
assert virtualMethods != null;
return Arrays.asList(virtualMethods);
}
@Override
void addVirtualMethods(Collection<DexEncodedMethod> methods) {
DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + methods.size()];
System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
int i = virtualMethods.length;
for (DexEncodedMethod method : methods) {
newMethods[i] = method;
i++;
}
virtualMethods = newMethods;
assert verifyNoDuplicateMethods();
}
@Override
void clearVirtualMethods() {
virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
}
@Override
void setVirtualMethods(DexEncodedMethod[] methods) {
virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
assert verifyNoDuplicateMethods();
}
@Override
void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
int vLen = virtualMethods.length;
int dLen = directMethods.length;
int mLen = privateInstanceMethods.size();
assert mLen <= dLen;
DexEncodedMethod[] newDirectMethods = new DexEncodedMethod[dLen - mLen];
int index = 0;
for (int i = 0; i < dLen; i++) {
DexEncodedMethod encodedMethod = directMethods[i];
if (!privateInstanceMethods.contains(encodedMethod)) {
newDirectMethods[index++] = encodedMethod;
}
}
assert index == dLen - mLen;
setDirectMethods(newDirectMethods);
DexEncodedMethod[] newVirtualMethods = new DexEncodedMethod[vLen + mLen];
System.arraycopy(virtualMethods, 0, newVirtualMethods, 0, vLen);
index = vLen;
for (DexEncodedMethod encodedMethod : privateInstanceMethods) {
newVirtualMethods[index++] = encodedMethod;
}
setVirtualMethods(newVirtualMethods);
}
@Override
DexEncodedMethod getDirectMethod(DexMethod method) {
for (DexEncodedMethod directMethod : directMethods) {
if (method.match(directMethod)) {
return directMethod;
}
}
return null;
}
@Override
DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
return PredicateUtils.findFirst(directMethods, predicate);
}
@Override
DexEncodedMethod getVirtualMethod(DexMethod method) {
for (DexEncodedMethod virtualMethod : virtualMethods) {
if (method.match(virtualMethod)) {
return virtualMethod;
}
}
return null;
}
@Override
DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
return PredicateUtils.findFirst(virtualMethods, predicate);
}
@Override
DexEncodedMethod getMethod(DexMethod method) {
DexEncodedMethod result = getDirectMethod(method);
return result == null ? getVirtualMethod(method) : result;
}
@Override
void addMethod(DexEncodedMethod method) {
if (belongsToDirectPool(method)) {
addDirectMethod(method);
} else {
addVirtualMethod(method);
}
}
@Override
void addVirtualMethod(DexEncodedMethod virtualMethod) {
assert belongsToVirtualPool(virtualMethod);
virtualMethods = ArrayUtils.appendSingleElement(virtualMethods, virtualMethod);
}
@Override
void addDirectMethod(DexEncodedMethod directMethod) {
assert belongsToDirectPool(directMethod);
directMethods = ArrayUtils.appendSingleElement(directMethods, directMethod);
}
@Override
public DexEncodedMethod replaceDirectMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
DexEncodedMethod newMethod = replaceMethod(method, replacement, directMethods);
assert newMethod == null || belongsToDirectPool(newMethod);
return newMethod;
}
@Override
public DexEncodedMethod replaceVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
DexEncodedMethod newMethod = replaceMethod(method, replacement, virtualMethods);
assert newMethod == null || belongsToVirtualPool(newMethod);
return newMethod;
}
private DexEncodedMethod replaceMethod(
DexMethod reference,
Function<DexEncodedMethod, DexEncodedMethod> replacement,
DexEncodedMethod[] methods) {
for (int i = 0; i < methods.length; i++) {
DexEncodedMethod method = methods[i];
if (reference.match(method)) {
DexEncodedMethod newMethod = replacement.apply(method);
methods[i] = newMethod;
return newMethod;
}
}
return null;
}
@Override
public DexEncodedMethod replaceDirectMethodWithVirtualMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
for (int i = 0; i < directMethods.length; i++) {
DexEncodedMethod directMethod = directMethods[i];
if (method.match(directMethod)) {
DexEncodedMethod newMethod = replacement.apply(directMethod);
assert belongsToVirtualPool(newMethod);
removeMethodWithIndex(
i, directMethods, newDirectMethods -> directMethods = newDirectMethods);
addVirtualMethod(newMethod);
return newMethod;
}
}
return null;
}
@Override
public void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
replaceDirectMethods(replacement);
replaceVirtualMethods(replacement);
}
@Override
public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
for (int i = 0; i < directMethods.length; i++) {
DexEncodedMethod method = directMethods[i];
DexEncodedMethod newMethod = replacement.apply(method);
assert newMethod != null;
if (method != newMethod) {
assert belongsToDirectPool(newMethod);
directMethods[i] = newMethod;
}
}
}
@Override
public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
for (int i = 0; i < virtualMethods.length; i++) {
DexEncodedMethod method = virtualMethods[i];
DexEncodedMethod newMethod = replacement.apply(method);
if (method != newMethod) {
assert belongsToVirtualPool(newMethod);
virtualMethods[i] = newMethod;
}
}
}
@Override
public void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
DexEncodedMethod[] oldMethods = directMethods;
clearDirectMethods();
DexEncodedMethod[] newMethods = new DexEncodedMethod[oldMethods.length];
for (int i = 0; i < oldMethods.length; i++) {
newMethods[i] = replacement.apply(oldMethods[i]);
}
directMethods = newMethods;
}
@Override
public void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
DexEncodedMethod[] oldMethods = virtualMethods;
clearVirtualMethods();
DexEncodedMethod[] newMethods = new DexEncodedMethod[oldMethods.length];
for (int i = 0; i < oldMethods.length; i++) {
newMethods[i] = replacement.apply(oldMethods[i]);
}
virtualMethods = newMethods;
}
}