Introduce a custom method collection with replaceable backing.
This CL is a pure refactoring moving the existing dual-array representation into
a backing structure. Follow-up work will simplify the backing API and introduce
a map-based backing.
Change-Id: I2dbe9af024ed3afdfd79a3eab1db0254e35adabf
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index d1e70ed..5f2b826 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -381,7 +381,7 @@
return false;
}
assert potentialHolder.isInterface();
- for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods) {
+ for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods()) {
if (virtualMethod.method.hasSameProtoAndName(method.method)
&& virtualMethod.accessFlags.isSameVisibility(method.accessFlags)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a75fd4e..4c0019b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.PredicateUtils;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -22,7 +21,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -37,8 +35,6 @@
void setMethod(int index, DexEncodedMethod method);
}
- private Optional<DexEncodedMethod> cachedClassInitializer = null;
-
public final Origin origin;
public DexType type;
public final ClassAccessFlags accessFlags;
@@ -55,10 +51,7 @@
protected DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
/** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
-
- /** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ protected final MethodCollection methodCollection;
/** Enclosing context of this class if it is an inner class, null otherwise. */
private EnclosingMethodAttribute enclosingMethod;
@@ -96,6 +89,7 @@
this.type = type;
setStaticFields(staticFields);
setInstanceFields(instanceFields);
+ this.methodCollection = new MethodCollection(this);
setDirectMethods(directMethods);
setVirtualMethods(virtualMethods);
this.nestHost = nestHost;
@@ -147,145 +141,59 @@
}
public List<DexEncodedMethod> directMethods() {
- assert directMethods != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(directMethods));
- }
- return Arrays.asList(directMethods);
+ return methodCollection.directMethods();
}
public Iterable<DexEncodedMethod> directMethods(Predicate<? super DexEncodedMethod> predicate) {
- return Iterables.filter(Arrays.asList(directMethods), predicate::test);
+ return Iterables.filter(directMethods(), predicate::test);
}
public void appendDirectMethod(DexEncodedMethod method) {
- cachedClassInitializer = null;
- DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + 1];
- System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
- newMethods[directMethods.length] = method;
- directMethods = newMethods;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendDirectMethod(method);
}
public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
- cachedClassInitializer = null;
- 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 verifyCorrectnessOfMethodHolders(methods);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendDirectMethods(methods);
}
public void removeDirectMethod(int index) {
- cachedClassInitializer = null;
- DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length - 1];
- System.arraycopy(directMethods, 0, newMethods, 0, index);
- System.arraycopy(directMethods, index + 1, newMethods, index, directMethods.length - index - 1);
- directMethods = newMethods;
+ methodCollection.removeDirectMethod(index);
}
public void removeDirectMethod(DexMethod method) {
- int index = -1;
- for (int i = 0; i < directMethods.length; i++) {
- if (method.match(directMethods[i])) {
- index = i;
- break;
- }
- }
- if (index >= 0) {
- removeDirectMethod(index);
- }
+ methodCollection.removeDirectMethod(method);
}
public void setDirectMethod(int index, DexEncodedMethod method) {
- cachedClassInitializer = null;
- directMethods[index] = method;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.setDirectMethod(index, method);
}
public void setDirectMethods(DexEncodedMethod[] methods) {
- cachedClassInitializer = null;
- directMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
- assert verifyCorrectnessOfMethodHolders(directMethods());
- assert verifyNoDuplicateMethods();
+ methodCollection.setDirectMethods(methods);
}
public List<DexEncodedMethod> virtualMethods() {
- assert virtualMethods != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(virtualMethods));
- }
- return Arrays.asList(virtualMethods);
+ return methodCollection.virtualMethods();
}
public Iterable<DexEncodedMethod> virtualMethods(Predicate<? super DexEncodedMethod> predicate) {
- return Iterables.filter(Arrays.asList(virtualMethods), predicate::test);
+ return Iterables.filter(virtualMethods(), predicate::test);
}
public void appendVirtualMethod(DexEncodedMethod method) {
- DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1];
- System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
- newMethods[virtualMethods.length] = method;
- virtualMethods = newMethods;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendVirtualMethod(method);
}
public void appendVirtualMethods(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 verifyCorrectnessOfMethodHolders(methods);
- assert verifyNoDuplicateMethods();
- }
-
- public void removeVirtualMethod(int index) {
- DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length - 1];
- System.arraycopy(virtualMethods, 0, newMethods, 0, index);
- System.arraycopy(
- virtualMethods, index + 1, newMethods, index, virtualMethods.length - index - 1);
- virtualMethods = newMethods;
+ methodCollection.appendVirtualMethods(methods);
}
public void setVirtualMethod(int index, DexEncodedMethod method) {
- virtualMethods[index] = method;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.setVirtualMethod(index, method);
}
public void setVirtualMethods(DexEncodedMethod[] methods) {
- virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
- assert verifyCorrectnessOfMethodHolders(virtualMethods());
- assert verifyNoDuplicateMethods();
- }
-
- private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
- assert method.method.holder == type
- : "Expected method `"
- + method.method.toSourceString()
- + "` to have holder `"
- + type.toSourceString()
- + "`";
- return true;
- }
-
- private boolean verifyCorrectnessOfMethodHolders(Iterable<DexEncodedMethod> methods) {
- for (DexEncodedMethod method : methods) {
- assert verifyCorrectnessOfMethodHolder(method);
- }
- return true;
+ methodCollection.setVirtualMethods(methods);
}
private boolean verifyNoAbstractMethodsOnNonAbstractClasses(
@@ -301,75 +209,16 @@
return true;
}
- private boolean verifyNoDuplicateMethods() {
- Set<DexMethod> unique = Sets.newIdentityHashSet();
- for (DexEncodedMethod method : methods()) {
- boolean changed = unique.add(method.method);
- assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
- }
- return true;
- }
-
public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
- for (DexEncodedMethod method : directMethods()) {
- consumer.accept(method);
- }
- for (DexEncodedMethod method : virtualMethods()) {
- consumer.accept(method);
- }
+ methodCollection.forEachMethod(consumer);
}
- public DexEncodedMethod[] allMethodsSorted() {
- int vLen = virtualMethods.length;
- int dLen = directMethods.length;
- DexEncodedMethod[] result = new DexEncodedMethod[vLen + dLen];
- System.arraycopy(virtualMethods, 0, result, 0, vLen);
- System.arraycopy(directMethods, 0, result, vLen, dLen);
- Arrays.sort(result,
- (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
- }
-
- public DexEncodedMethod[] directMethodsSorted() {
- DexEncodedMethod[] result = new DexEncodedMethod[directMethods.length];
- System.arraycopy(directMethods, 0, result, 0, directMethods.length);
- Arrays.sort(
- result, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
- }
-
- public DexEncodedMethod[] virtualMethodsSorted() {
- DexEncodedMethod[] result = new DexEncodedMethod[virtualMethods.length];
- System.arraycopy(virtualMethods, 0, result, 0, virtualMethods.length);
- Arrays.sort(
- result, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
+ public List<DexEncodedMethod> allMethodsSorted() {
+ return methodCollection.allMethodsSorted();
}
public 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);
+ methodCollection.virtualizeMethods(privateInstanceMethods);
}
/**
@@ -561,28 +410,27 @@
/** Find direct method in this class matching {@param method}. */
public DexEncodedMethod lookupDirectMethod(DexMethod method) {
- return lookupTarget(directMethods, method);
+ return methodCollection.getDirectMethod(method);
}
/** Find direct method in this class matching {@param predicate}. */
public DexEncodedMethod lookupDirectMethod(Predicate<DexEncodedMethod> predicate) {
- return PredicateUtils.findFirst(directMethods, predicate);
+ return methodCollection.getDirectMethod(predicate);
}
/** Find virtual method in this class matching {@param method}. */
public DexEncodedMethod lookupVirtualMethod(DexMethod method) {
- return lookupTarget(virtualMethods, method);
+ return methodCollection.getVirtualMethod(method);
}
/** Find virtual method in this class matching {@param predicate}. */
public DexEncodedMethod lookupVirtualMethod(Predicate<DexEncodedMethod> predicate) {
- return PredicateUtils.findFirst(virtualMethods, predicate);
+ return methodCollection.getVirtualMethod(predicate);
}
/** Find method in this class matching {@param method}. */
public DexEncodedMethod lookupMethod(DexMethod method) {
- DexEncodedMethod result = lookupDirectMethod(method);
- return result == null ? lookupVirtualMethod(method) : result;
+ return methodCollection.getMethod(method);
}
public DexEncodedMethod lookupSignaturePolymorphicMethod(
@@ -592,7 +440,7 @@
}
DexEncodedMethod matchingName = null;
DexEncodedMethod signaturePolymorphicMethod = null;
- for (DexEncodedMethod method : virtualMethods) {
+ for (DexEncodedMethod method : virtualMethods()) {
if (method.method.name == methodName) {
if (matchingName != null) {
// The jvm spec, section 5.4.3.3 details that there must be exactly one method with the
@@ -701,16 +549,7 @@
}
public synchronized DexEncodedMethod getClassInitializer() {
- if (cachedClassInitializer == null) {
- cachedClassInitializer = Optional.empty();
- for (DexEncodedMethod directMethod : directMethods) {
- if (directMethod.isClassInitializer()) {
- cachedClassInitializer = Optional.of(directMethod);
- break;
- }
- }
- }
- return cachedClassInitializer.orElse(null);
+ return methodCollection.getClassInitializer();
}
public Origin getOrigin() {
@@ -1004,8 +843,7 @@
assert !isInterface() || virtualMethods().stream().noneMatch(DexEncodedMethod::isFinal);
assert verifyCorrectnessOfFieldHolders(fields());
assert verifyNoDuplicateFields();
- assert verifyCorrectnessOfMethodHolders(methods());
- assert verifyNoDuplicateMethods();
+ assert methodCollection.verify();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 78a59c2..35f209a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -160,8 +160,9 @@
}
synchronizedCollectAll(indexedItems, staticFields);
synchronizedCollectAll(indexedItems, instanceFields);
- synchronizedCollectAll(indexedItems, directMethods);
- synchronizedCollectAll(indexedItems, virtualMethods);
+ synchronized (methodCollection) {
+ methodCollection.forEachMethod(m -> m.collectIndexedItems(indexedItems));
+ }
}
}
@@ -196,8 +197,9 @@
// The ordering of methods and fields may not be deterministic due to concurrency
// (see b/116027780).
sortMembers();
- synchronizedCollectAll(collector, directMethods);
- synchronizedCollectAll(collector, virtualMethods);
+ synchronized (methodCollection) {
+ methodCollection.forEachMethod(m -> m.collectMixedSectionItems(collector));
+ }
synchronizedCollectAll(collector, staticFields);
synchronizedCollectAll(collector, instanceFields);
}
@@ -278,7 +280,7 @@
}
public boolean hasMethods() {
- return directMethods.length + virtualMethods.length > 0;
+ return methodCollection.size() > 0;
}
public boolean hasMethodsOrFields() {
@@ -287,15 +289,13 @@
public boolean hasAnnotations() {
return !annotations().isEmpty()
- || hasAnnotations(virtualMethods)
- || hasAnnotations(directMethods)
+ || hasAnnotations(methodCollection)
|| hasAnnotations(staticFields)
|| hasAnnotations(instanceFields);
}
boolean hasOnlyInternalizableAnnotations() {
- return !hasAnnotations(virtualMethods)
- && !hasAnnotations(directMethods)
+ return !hasAnnotations(methodCollection)
&& !hasAnnotations(staticFields)
&& !hasAnnotations(instanceFields);
}
@@ -306,9 +306,9 @@
}
}
- private boolean hasAnnotations(DexEncodedMethod[] methods) {
+ private boolean hasAnnotations(MethodCollection methods) {
synchronized (methods) {
- return Arrays.stream(methods).anyMatch(DexEncodedMethod::hasAnnotation);
+ return methods.hasAnnotations();
}
}
@@ -346,10 +346,7 @@
}
public boolean isSorted() {
- return isSorted(virtualMethods)
- && isSorted(directMethods)
- && isSorted(staticFields)
- && isSorted(instanceFields);
+ return methodCollection.isSorted() && isSorted(staticFields) && isSorted(instanceFields);
}
private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> boolean isSorted(
@@ -360,6 +357,7 @@
return Arrays.equals(items, sorted);
}
}
+
public DexEncodedArray getStaticValues() {
// The sentinel value is left over for classes that actually have no fields.
if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
@@ -370,35 +368,21 @@
}
public void addMethod(DexEncodedMethod method) {
- if (method.accessFlags.isStatic()
- || method.accessFlags.isPrivate()
- || method.accessFlags.isConstructor()) {
- addDirectMethod(method);
- } else {
- addVirtualMethod(method);
- }
+ methodCollection.addMethod(method);
}
public void addVirtualMethod(DexEncodedMethod virtualMethod) {
- assert !virtualMethod.accessFlags.isStatic();
- assert !virtualMethod.accessFlags.isPrivate();
- assert !virtualMethod.accessFlags.isConstructor();
- virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
- virtualMethods[virtualMethods.length - 1] = virtualMethod;
+ methodCollection.addVirtualMethod(virtualMethod);
}
- public void addDirectMethod(DexEncodedMethod staticMethod) {
- assert staticMethod.accessFlags.isStatic() || staticMethod.accessFlags.isPrivate()
- || staticMethod.accessFlags.isConstructor();
- directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
- directMethods[directMethods.length - 1] = staticMethod;
+ public void addDirectMethod(DexEncodedMethod directMethod) {
+ methodCollection.addDirectMethod(directMethod);
}
public void sortMembers() {
sortEncodedFields(staticFields);
sortEncodedFields(instanceFields);
- sortEncodedMethods(directMethods);
- sortEncodedMethods(virtualMethods);
+ methodCollection.sort();
}
private void sortEncodedFields(DexEncodedField[] fields) {
@@ -407,12 +391,6 @@
}
}
- private void sortEncodedMethods(DexEncodedMethod[] methods) {
- synchronized (methods) {
- Arrays.sort(methods, Comparator.comparing(a -> a.method));
- }
- }
-
@Override
public DexProgramClass get() {
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
new file mode 100644
index 0000000..4dc36b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -0,0 +1,258 @@
+// 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.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class MethodArrayBacking {
+
+ private DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ private DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+
+ int size() {
+ return directMethods.length + virtualMethods.length;
+ }
+
+ void forEachMethod(Consumer<DexEncodedMethod> consumer) {
+ for (DexEncodedMethod method : directMethods()) {
+ consumer.accept(method);
+ }
+ for (DexEncodedMethod method : virtualMethods()) {
+ consumer.accept(method);
+ }
+ }
+
+ List<DexEncodedMethod> directMethods() {
+ assert directMethods != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(directMethods));
+ }
+ return Arrays.asList(directMethods);
+ }
+
+ void appendDirectMethod(DexEncodedMethod method) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + 1];
+ System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
+ newMethods[directMethods.length] = method;
+ directMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void appendDirectMethods(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();
+ }
+
+ void removeDirectMethod(int index) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length - 1];
+ System.arraycopy(directMethods, 0, newMethods, 0, index);
+ System.arraycopy(directMethods, index + 1, newMethods, index, directMethods.length - index - 1);
+ directMethods = newMethods;
+ }
+
+ void removeDirectMethod(DexMethod method) {
+ int index = -1;
+ for (int i = 0; i < directMethods.length; i++) {
+ if (method.match(directMethods[i])) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ removeDirectMethod(index);
+ }
+ }
+
+ void setDirectMethod(int index, DexEncodedMethod method) {
+ directMethods[index] = method;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void setDirectMethods(DexEncodedMethod[] methods) {
+ directMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
+ assert verifyNoDuplicateMethods();
+ }
+
+ List<DexEncodedMethod> virtualMethods() {
+ assert virtualMethods != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(virtualMethods));
+ }
+ return Arrays.asList(virtualMethods);
+ }
+
+ void appendVirtualMethod(DexEncodedMethod method) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1];
+ System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
+ newMethods[virtualMethods.length] = method;
+ virtualMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void appendVirtualMethods(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();
+ }
+
+ void setVirtualMethod(int index, DexEncodedMethod method) {
+ virtualMethods[index] = method;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void setVirtualMethods(DexEncodedMethod[] methods) {
+ virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
+ assert verifyNoDuplicateMethods();
+ }
+
+ 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);
+ }
+
+ DexEncodedMethod getDirectMethod(DexMethod method) {
+ for (DexEncodedMethod directMethod : directMethods) {
+ if (method.match(directMethod)) {
+ return directMethod;
+ }
+ }
+ return null;
+ }
+
+ DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
+ return PredicateUtils.findFirst(directMethods, predicate);
+ }
+
+ DexEncodedMethod getVirtualMethod(DexMethod method) {
+ for (DexEncodedMethod virtualMethod : virtualMethods) {
+ if (method.match(virtualMethod)) {
+ return virtualMethod;
+ }
+ }
+ return null;
+ }
+
+ DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
+ return PredicateUtils.findFirst(virtualMethods, predicate);
+ }
+
+ DexEncodedMethod getMethod(DexMethod method) {
+ DexEncodedMethod result = getDirectMethod(method);
+ return result == null ? getVirtualMethod(method) : result;
+ }
+
+ void addMethod(DexEncodedMethod method) {
+ if (method.accessFlags.isStatic()
+ || method.accessFlags.isPrivate()
+ || method.accessFlags.isConstructor()) {
+ addDirectMethod(method);
+ } else {
+ addVirtualMethod(method);
+ }
+ }
+
+ void addVirtualMethod(DexEncodedMethod virtualMethod) {
+ assert !virtualMethod.accessFlags.isStatic();
+ assert !virtualMethod.accessFlags.isPrivate();
+ assert !virtualMethod.accessFlags.isConstructor();
+ virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
+ virtualMethods[virtualMethods.length - 1] = virtualMethod;
+ }
+
+ void addDirectMethod(DexEncodedMethod staticMethod) {
+ assert staticMethod.accessFlags.isStatic()
+ || staticMethod.accessFlags.isPrivate()
+ || staticMethod.accessFlags.isConstructor();
+ directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
+ directMethods[directMethods.length - 1] = staticMethod;
+ }
+
+ boolean verifyNoDuplicateMethods() {
+ Set<DexMethod> unique = Sets.newIdentityHashSet();
+ forEachMethod(
+ method -> {
+ boolean changed = unique.add(method.method);
+ assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
+ });
+ return true;
+ }
+
+ boolean hasAnnotations() {
+ for (DexEncodedMethod method : virtualMethods) {
+ if (method.hasAnnotation()) {
+ return true;
+ }
+ }
+ for (DexEncodedMethod method : directMethods) {
+ if (method.hasAnnotation()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ synchronized boolean isSorted() {
+ return isSorted(virtualMethods) && isSorted(directMethods);
+ }
+
+ private static boolean isSorted(DexEncodedMethod[] items) {
+ DexEncodedMethod[] sorted = items.clone();
+ Arrays.sort(sorted, Comparator.comparing(DexEncodedMember::toReference));
+ return Arrays.equals(items, sorted);
+ }
+
+ synchronized void sort() {
+ synchronized (virtualMethods) {
+ Arrays.sort(virtualMethods, Comparator.comparing(a -> a.method));
+ }
+ synchronized (directMethods) {
+ Arrays.sort(directMethods, Comparator.comparing(a -> a.method));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
new file mode 100644
index 0000000..4d4fb2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -0,0 +1,191 @@
+package com.android.tools.r8.graph;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class MethodCollection {
+
+ private final DexClass holder;
+ private final MethodArrayBacking backing = new MethodArrayBacking();
+ private Optional<DexEncodedMethod> cachedClassInitializer = null;
+
+ public MethodCollection(DexClass holder) {
+ this.holder = holder;
+ }
+
+ public int size() {
+ return backing.size();
+ }
+
+ public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
+ backing.forEachMethod(consumer);
+ }
+
+ public List<DexEncodedMethod> allMethodsSorted() {
+ List<DexEncodedMethod> sorted = new ArrayList<>(size());
+ forEachMethod(sorted::add);
+ sorted.sort((a, b) -> a.method.slowCompareTo(b.method));
+ return sorted;
+ }
+
+ public List<DexEncodedMethod> directMethods() {
+ return backing.directMethods();
+ }
+
+ public List<DexEncodedMethod> virtualMethods() {
+ return backing.virtualMethods();
+ }
+
+ public DexEncodedMethod getMethod(DexMethod method) {
+ return backing.getMethod(method);
+ }
+
+ 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);
+ }
+
+ public DexEncodedMethod getClassInitializer() {
+ if (cachedClassInitializer == null) {
+ cachedClassInitializer = Optional.empty();
+ for (DexEncodedMethod directMethod : directMethods()) {
+ if (directMethod.isClassInitializer()) {
+ cachedClassInitializer = Optional.of(directMethod);
+ break;
+ }
+ }
+ }
+ return cachedClassInitializer.orElse(null);
+ }
+
+ public void addMethod(DexEncodedMethod method) {
+ backing.addMethod(method);
+ }
+
+ public void addVirtualMethod(DexEncodedMethod virtualMethod) {
+ backing.addVirtualMethod(virtualMethod);
+ }
+
+ public void addDirectMethod(DexEncodedMethod directMethod) {
+ backing.addDirectMethod(directMethod);
+ }
+
+ public void appendDirectMethod(DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ cachedClassInitializer = null;
+ backing.appendDirectMethod(method);
+ }
+
+ public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ cachedClassInitializer = null;
+ backing.appendDirectMethods(methods);
+ }
+
+ public void removeDirectMethod(int index) {
+ cachedClassInitializer = null;
+ backing.removeDirectMethod(index);
+ }
+
+ public void removeDirectMethod(DexMethod method) {
+ backing.removeDirectMethod(method);
+ }
+
+ public void setDirectMethod(int index, DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ cachedClassInitializer = null;
+ backing.setDirectMethod(index, method);
+ }
+
+ public void setDirectMethods(DexEncodedMethod[] methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ cachedClassInitializer = null;
+ backing.setDirectMethods(methods);
+ }
+
+ public void appendVirtualMethod(DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ backing.appendVirtualMethod(method);
+ }
+
+ public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ backing.appendVirtualMethods(methods);
+ }
+
+ public void setVirtualMethod(int index, DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ backing.setVirtualMethod(index, method);
+ }
+
+ public void setVirtualMethods(DexEncodedMethod[] methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ backing.setVirtualMethods(methods);
+ }
+
+ public void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
+ backing.virtualizeMethods(privateInstanceMethods);
+ }
+
+ public boolean hasAnnotations() {
+ return backing.hasAnnotations();
+ }
+
+ public boolean isSorted() {
+ return backing.isSorted();
+ }
+
+ public void sort() {
+ backing.sort();
+ }
+
+ public boolean verify() {
+ forEachMethod(
+ method -> {
+ assert verifyCorrectnessOfMethodHolder(method);
+ });
+ assert backing.verifyNoDuplicateMethods();
+ return true;
+ }
+
+ private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
+ assert method.method.holder == holder.type
+ : "Expected method `"
+ + method.method.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;
+ }
+}