| // 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(DexProto methodProto, DexString methodName) { |
| DexEncodedMethod directMethod = internalGetMethod(methodProto, methodName, directMethods); |
| return directMethod == null |
| ? internalGetMethod(methodProto, methodName, virtualMethods) |
| : directMethod; |
| } |
| |
| private static DexEncodedMethod internalGetMethod( |
| DexProto methodProto, DexString methodName, DexEncodedMethod[] methods) { |
| for (DexEncodedMethod method : methods) { |
| if (method.getReference().match(methodProto, methodName)) { |
| return method; |
| } |
| } |
| return null; |
| } |
| |
| @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; |
| } |
| } |