Extend abstract base for method collection backings.

Change-Id: Iefd6fe1ef2951717f86026a9075c62e059353f5a
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 773a71a..6c74124 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -148,12 +148,8 @@
     return Iterables.filter(directMethods(), predicate::test);
   }
 
-  public void appendDirectMethod(DexEncodedMethod method) {
-    methodCollection.appendDirectMethod(method);
-  }
-
-  public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
-    methodCollection.appendDirectMethods(methods);
+  public void addDirectMethods(Collection<DexEncodedMethod> methods) {
+    methodCollection.addDirectMethods(methods);
   }
 
   public void removeDirectMethod(DexMethod method) {
@@ -172,12 +168,8 @@
     return Iterables.filter(virtualMethods(), predicate::test);
   }
 
-  public void appendVirtualMethod(DexEncodedMethod method) {
-    methodCollection.appendVirtualMethod(method);
-  }
-
-  public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
-    methodCollection.appendVirtualMethods(methods);
+  public void addVirtualMethods(Collection<DexEncodedMethod> methods) {
+    methodCollection.addVirtualMethods(methods);
   }
 
   public void setVirtualMethods(DexEncodedMethod[] methods) {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
index 96059b4..7f87c94 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -30,6 +30,23 @@
     return !belongsToDirectPool(method);
   }
 
+  private boolean verifyNoDuplicateMethods() {
+    Set<DexMethod> unique = Sets.newIdentityHashSet();
+    forEachMethod(
+        method -> {
+          boolean changed = unique.add(method.method);
+          assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
+        });
+    return true;
+  }
+
+  @Override
+  boolean verify() {
+    assert verifyNoDuplicateMethods();
+    return true;
+  }
+
+  @Override
   int size() {
     return directMethods.length + virtualMethods.length;
   }
@@ -51,24 +68,19 @@
     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);
   }
 
-  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) {
+  @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;
@@ -87,6 +99,7 @@
     directMethods = newMethods;
   }
 
+  @Override
   void removeDirectMethod(DexMethod method) {
     int index = -1;
     for (int i = 0; i < directMethods.length; i++) {
@@ -100,25 +113,20 @@
     }
   }
 
+  @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);
   }
 
-  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) {
+  @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;
@@ -130,11 +138,13 @@
     assert verifyNoDuplicateMethods();
   }
 
+  @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;
@@ -161,6 +171,7 @@
     setVirtualMethods(newVirtualMethods);
   }
 
+  @Override
   DexEncodedMethod getDirectMethod(DexMethod method) {
     for (DexEncodedMethod directMethod : directMethods) {
       if (method.match(directMethod)) {
@@ -170,10 +181,12 @@
     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)) {
@@ -183,51 +196,41 @@
     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 (method.accessFlags.isStatic()
-        || method.accessFlags.isPrivate()
-        || method.accessFlags.isConstructor()) {
+    if (belongsToDirectPool(method)) {
       addDirectMethod(method);
     } else {
       addVirtualMethod(method);
     }
   }
 
+  @Override
   void addVirtualMethod(DexEncodedMethod virtualMethod) {
-    assert !virtualMethod.accessFlags.isStatic();
-    assert !virtualMethod.accessFlags.isPrivate();
-    assert !virtualMethod.accessFlags.isConstructor();
+    assert belongsToVirtualPool(virtualMethod);
     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();
+  @Override
+  void addDirectMethod(DexEncodedMethod directMethod) {
+    assert belongsToDirectPool(directMethod);
     directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
-    directMethods[directMethods.length - 1] = staticMethod;
+    directMethods[directMethods.length - 1] = directMethod;
   }
 
-  boolean verifyNoDuplicateMethods() {
-    Set<DexMethod> unique = Sets.newIdentityHashSet();
-    forEachMethod(
-        method -> {
-          boolean changed = unique.add(method.method);
-          assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
-        });
-    return true;
-  }
-
+  @Override
   public DexEncodedMethod replaceDirectMethod(
       DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
     for (int i = 0; i < directMethods.length; i++) {
@@ -242,6 +245,7 @@
     return null;
   }
 
+  @Override
   public DexEncodedMethod replaceDirectMethodWithVirtualMethod(
       DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
     for (int i = 0; i < directMethods.length; i++) {
@@ -250,18 +254,20 @@
         DexEncodedMethod newMethod = replacement.apply(directMethod);
         assert belongsToVirtualPool(newMethod);
         removeDirectMethod(i);
-        appendVirtualMethod(newMethod);
+        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];
@@ -274,6 +280,7 @@
     }
   }
 
+  @Override
   public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
     for (int i = 0; i < virtualMethods.length; i++) {
       DexEncodedMethod method = virtualMethods[i];
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 7cfc97c..54b212f 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -16,7 +16,7 @@
 public class MethodCollection {
 
   private final DexClass holder;
-  private final MethodArrayBacking backing = new MethodArrayBacking();
+  private final MethodCollectionBacking backing = new MethodArrayBacking();
   private DexEncodedMethod cachedClassInitializer = DexEncodedMethod.SENTINEL;
 
   public MethodCollection(DexClass holder) {
@@ -163,16 +163,10 @@
     return backing.replaceDirectMethodWithVirtualMethod(method, replacement);
   }
 
-  public void appendDirectMethod(DexEncodedMethod method) {
-    assert verifyCorrectnessOfMethodHolder(method);
-    resetDirectMethodCaches();
-    backing.appendDirectMethod(method);
-  }
-
-  public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
+  public void addDirectMethods(Collection<DexEncodedMethod> methods) {
     assert verifyCorrectnessOfMethodHolders(methods);
     resetDirectMethodCaches();
-    backing.appendDirectMethods(methods);
+    backing.addDirectMethods(methods);
   }
 
   public void removeDirectMethod(DexMethod method) {
@@ -186,16 +180,10 @@
     backing.setDirectMethods(methods);
   }
 
-  public void appendVirtualMethod(DexEncodedMethod method) {
-    assert verifyCorrectnessOfMethodHolder(method);
-    resetVirtualMethodCaches();
-    backing.appendVirtualMethod(method);
-  }
-
-  public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
+  public void addVirtualMethods(Collection<DexEncodedMethod> methods) {
     assert verifyCorrectnessOfMethodHolders(methods);
     resetVirtualMethodCaches();
-    backing.appendVirtualMethods(methods);
+    backing.addVirtualMethods(methods);
   }
 
   public void setVirtualMethods(DexEncodedMethod[] methods) {
@@ -223,7 +211,7 @@
         method -> {
           assert verifyCorrectnessOfMethodHolder(method);
         });
-    assert backing.verifyNoDuplicateMethods();
+    assert backing.verify();
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
index b50adf6..09de861 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -4,11 +4,25 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.utils.TraversalContinuation;
+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 abstract class MethodCollectionBacking {
 
+  // Internal consistency.
+
+  abstract boolean verify();
+
+  // Collection methods.
+
+  abstract int size();
+
+  // Traversal methods.
+
   abstract TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn);
 
   void forEachMethod(Consumer<DexEncodedMethod> fn) {
@@ -18,4 +32,58 @@
           return TraversalContinuation.CONTINUE;
         });
   }
+
+  abstract Iterable<DexEncodedMethod> methods();
+
+  abstract List<DexEncodedMethod> directMethods();
+
+  abstract List<DexEncodedMethod> virtualMethods();
+
+  // Lookup methods.
+
+  abstract DexEncodedMethod getMethod(DexMethod method);
+
+  abstract DexEncodedMethod getDirectMethod(DexMethod method);
+
+  abstract DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate);
+
+  abstract DexEncodedMethod getVirtualMethod(DexMethod method);
+
+  abstract DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate);
+
+  // Amendment methods.
+
+  abstract void addMethod(DexEncodedMethod method);
+
+  abstract void addDirectMethod(DexEncodedMethod method);
+
+  abstract void addVirtualMethod(DexEncodedMethod method);
+
+  abstract void addDirectMethods(Collection<DexEncodedMethod> methods);
+
+  abstract void addVirtualMethods(Collection<DexEncodedMethod> methods);
+
+  // Removal methods.
+
+  abstract void removeDirectMethod(DexMethod method);
+
+  // Replacement/mutation methods.
+
+  abstract void setDirectMethods(DexEncodedMethod[] methods);
+
+  abstract void setVirtualMethods(DexEncodedMethod[] methods);
+
+  abstract void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
+  abstract void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
+  abstract void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
+  abstract DexEncodedMethod replaceDirectMethod(
+      DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
+  abstract DexEncodedMethod replaceDirectMethodWithVirtualMethod(
+      DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
+  abstract void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 76e97df..3d16d19 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -229,7 +229,7 @@
     for (DexProgramClass clazz : newSyntheticMethods.keySet()) {
       List<DexEncodedMethod> newForwardingMethods = newSyntheticMethods.get(clazz);
       if (newForwardingMethods != null) {
-        clazz.appendVirtualMethods(newForwardingMethods);
+        clazz.addVirtualMethods(newForwardingMethods);
         newForwardingMethods.forEach(newSynthesizedMethodConsumer);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index efd6f7d..2be1832 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -99,7 +99,7 @@
           method.annotations().keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)));
     }
     // Add the newly constructed methods to the class.
-    clazz.appendVirtualMethods(covariantReturnTypeMethods);
+    clazz.addVirtualMethods(covariantReturnTypeMethods);
   }
 
   // Processes all the dalvik.annotation.codegen.CovariantReturnType and dalvik.annotation.codegen.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index ceed9ef..fb97d0c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -294,7 +294,7 @@
     for (DexClass dexClass : callBackMethods.keySet()) {
       List<DexEncodedMethod> dexEncodedMethods =
           generateCallbackMethods(callBackMethods.get(dexClass), dexClass);
-      dexClass.appendVirtualMethods(dexEncodedMethods);
+      dexClass.addVirtualMethods(dexEncodedMethods);
       result.addAll(dexEncodedMethods);
     }
     return result;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index ec1b05f..87c3424 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -667,14 +667,14 @@
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
-      DexClass implMethodHolder = definitionFor(implMethod.holder);
+      DexProgramClass implMethodHolder = definitionFor(implMethod.holder).asProgramClass();
       return allowMethodModification
           ? modifyLambdaImplementationMethod(implMethod, implMethodHolder)
           : createSyntheticAccessor(implMethod, implMethodHolder);
     }
 
     private DexEncodedMethod modifyLambdaImplementationMethod(
-        DexMethod implMethod, DexClass implMethodHolder) {
+        DexMethod implMethod, DexProgramClass implMethodHolder) {
       return implMethodHolder
           .getMethodCollection()
           .replaceDirectMethodWithVirtualMethod(
@@ -701,7 +701,7 @@
     }
 
     private DexEncodedMethod createSyntheticAccessor(
-        DexMethod implMethod, DexClass implMethodHolder) {
+        DexMethod implMethod, DexProgramClass implMethodHolder) {
       MethodAccessFlags accessorFlags =
           MethodAccessFlags.fromSharedAccessFlags(
               Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC, false);
@@ -725,7 +725,7 @@
                   registry -> registry.registerInvokeDirect(implMethod)),
               true);
 
-      implMethodHolder.appendVirtualMethod(accessorEncodedMethod);
+      implMethodHolder.addVirtualMethod(accessorEncodedMethod);
       return accessorEncodedMethod;
     }
   }
@@ -761,7 +761,7 @@
 
       // We may arrive here concurrently so we need must update the methods of the class atomically.
       synchronized (accessorClass) {
-        accessorClass.appendDirectMethod(accessorEncodedMethod);
+        accessorClass.addDirectMethod(accessorEncodedMethod);
       }
 
       return accessorEncodedMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index e0ca9093..9c8b4d5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -724,7 +724,7 @@
           methodMapping.put(originalMethod, newMethod.method);
         }
       }
-      hostClass.appendDirectMethods(newMethods);
+      hostClass.addDirectMethods(newMethods);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 1dd374d..bebaf56 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2534,7 +2534,7 @@
       DexProgramClass holder = bridge.getHolder();
       DexEncodedMethod method = bridge.getMethod();
       appView.appInfo().invalidateTypeCacheFor(holder.type);
-      holder.appendVirtualMethod(method);
+      holder.addVirtualMethod(method);
       targetedMethods.add(method, graphReporter.fakeReportShouldNotBeUsed());
       liveMethods.add(holder, method, graphReporter.fakeReportShouldNotBeUsed());
       pinnedItems.add(method.method);
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index b89678e..5031d82 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -471,9 +471,9 @@
     numberOfMergedClasses++;
 
     // Move members from source to target.
-    targetClass.appendDirectMethods(
+    targetClass.addDirectMethods(
         mergeMethods(sourceClass.directMethods(), targetClass.directMethods(), targetClass));
-    targetClass.appendVirtualMethods(
+    targetClass.addVirtualMethods(
         mergeMethods(sourceClass.virtualMethods(), targetClass.virtualMethods(), targetClass));
     targetClass.setStaticFields(
         mergeFields(sourceClass.staticFields(), targetClass.staticFields(), targetClass));
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index ef0ce63..fde562c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1085,8 +1085,8 @@
               ? DexTypeList.empty()
               : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
       // Step 2: replace fields and methods.
-      target.appendDirectMethods(directMethods.values());
-      target.appendVirtualMethods(virtualMethods.values());
+      target.addDirectMethods(directMethods.values());
+      target.addVirtualMethods(virtualMethods.values());
       target.setInstanceFields(mergedInstanceFields);
       target.setStaticFields(mergedStaticFields);
       target.forEachField(feedback::markFieldCannotBeKept);