Merge "Create field and method arrays directly in class merger"
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 6f935ee..ccc1ccf 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -37,7 +37,6 @@
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterators;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -55,7 +54,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -697,34 +695,36 @@
         deferredRenamings.map(virtualMethod.method, shadowedBy.method);
       }
 
-      Collection<DexEncodedMethod> mergedDirectMethods =
-          mergeItems(
-              directMethods.values().iterator(),
-              target.directMethods(),
-              MethodSignatureEquivalence.get(),
-              this::resolveMethodConflict);
-      Collection<DexEncodedMethod> mergedVirtualMethods =
-          mergeItems(
-              virtualMethods.values().iterator(),
-              target.virtualMethods(),
-              MethodSignatureEquivalence.get(),
-              this::resolveMethodConflict);
       if (abortMerge) {
         return false;
       }
+
+      DexEncodedMethod[] mergedDirectMethods =
+          mergeMethods(directMethods.values(), target.directMethods());
+      DexEncodedMethod[] mergedVirtualMethods =
+          mergeMethods(virtualMethods.values(), target.virtualMethods());
+
       // Step 2: Merge fields
-      Collection<DexEncodedField> mergedStaticFields =
-          mergeItems(
-              Iterators.forArray(source.staticFields()),
-              target.staticFields(),
-              FieldSignatureEquivalence.get(),
-              this::resolveFieldConflict);
-      Collection<DexEncodedField> mergedInstanceFields =
-          mergeItems(
-              Iterators.forArray(source.instanceFields()),
+      Set<Wrapper<DexField>> existingFieldSignatures = new HashSet<>();
+      addAll(existingFieldSignatures, target.fields(), FieldSignatureEquivalence.get());
+
+      Predicate<DexField> availableFieldSignatures =
+          field -> !existingFieldSignatures.contains(FieldSignatureEquivalence.get().wrap(field));
+
+      DexEncodedField[] mergedInstanceFields =
+          mergeFields(
+              source.instanceFields(),
               target.instanceFields(),
-              FieldSignatureEquivalence.get(),
-              this::resolveFieldConflict);
+              availableFieldSignatures,
+              existingFieldSignatures);
+
+      DexEncodedField[] mergedStaticFields =
+          mergeFields(
+              source.staticFields(),
+              target.staticFields(),
+              availableFieldSignatures,
+              existingFieldSignatures);
+
       // Step 3: Merge interfaces
       Set<DexType> interfaces = mergeArrays(target.interfaces.values, source.interfaces.values);
       // Now destructively update the class.
@@ -739,14 +739,10 @@
           ? DexTypeList.empty()
           : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()]));
       // Step 2: replace fields and methods.
-      target.setDirectMethods(mergedDirectMethods
-          .toArray(new DexEncodedMethod[mergedDirectMethods.size()]));
-      target.setVirtualMethods(mergedVirtualMethods
-          .toArray(new DexEncodedMethod[mergedVirtualMethods.size()]));
-      target.setStaticFields(mergedStaticFields
-          .toArray(new DexEncodedField[mergedStaticFields.size()]));
-      target.setInstanceFields(mergedInstanceFields
-          .toArray(new DexEncodedField[mergedInstanceFields.size()]));
+      target.setDirectMethods(mergedDirectMethods);
+      target.setVirtualMethods(mergedVirtualMethods);
+      target.setInstanceFields(mergedInstanceFields);
+      target.setStaticFields(mergedStaticFields);
       // Step 3: Unlink old class to ease tree shaking.
       source.superType = application.dexItemFactory.objectType;
       source.setDirectMethods(null);
@@ -889,41 +885,38 @@
       return merged;
     }
 
-    private <T extends PresortedComparable<T>, S extends KeyedDexItem<T>> Collection<S> mergeItems(
-        Iterator<S> fromItems,
-        S[] toItems,
-        Equivalence<T> equivalence,
-        BiFunction<S, Predicate<T>, S> onConflict) {
-      Map<Wrapper<T>, S> map = new HashMap<>();
-      // First add everything from the target class. These items are not preprocessed.
-      for (S item : toItems) {
-        map.put(equivalence.wrap(item.getKey()), item);
+    private DexEncodedField[] mergeFields(
+        DexEncodedField[] sourceFields,
+        DexEncodedField[] targetFields,
+        Predicate<DexField> availableFieldSignatures,
+        Set<Wrapper<DexField>> existingFieldSignatures) {
+      DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+      // Add fields from source
+      int i = 0;
+      for (DexEncodedField field : sourceFields) {
+        DexEncodedField resultingField = renameFieldIfNeeded(field, availableFieldSignatures);
+        existingFieldSignatures.add(FieldSignatureEquivalence.get().wrap(resultingField.field));
+        deferredRenamings.map(field.field, resultingField.field);
+        result[i] = resultingField;
+        i++;
       }
-      // Now add the new items, resolving shadowing.
-      addNonShadowed(fromItems, map, equivalence, onConflict);
-      return map.values();
+      // Add fields from target.
+      System.arraycopy(targetFields, 0, result, i, targetFields.length);
+      return result;
     }
 
-    private <T extends PresortedComparable<T>, S extends KeyedDexItem<T>> void addNonShadowed(
-        Iterator<S> items,
-        Map<Wrapper<T>, S> map,
-        Equivalence<T> equivalence,
-        BiFunction<S, Predicate<T>, S> onConflict) {
-      Predicate<T> availableSignatures = key -> !map.containsKey(equivalence.wrap(key));
-      while (items.hasNext()) {
-        S item = items.next();
-        assert item != null;
-        Wrapper<T> wrapped = equivalence.wrap(item.getKey());
-        if (map.containsKey(wrapped)) {
-          S resolved = onConflict.apply(item, availableSignatures);
-          assert availableSignatures.test(resolved.getKey());
-          wrapped = equivalence.wrap(resolved.getKey());
-          map.put(wrapped, resolved);
-          assert !availableSignatures.test(resolved.getKey());
-        } else {
-          map.put(wrapped, item);
-        }
+    private DexEncodedMethod[] mergeMethods(
+        Collection<DexEncodedMethod> sourceMethods, DexEncodedMethod[] targetMethods) {
+      DexEncodedMethod[] result = new DexEncodedMethod[sourceMethods.size() + targetMethods.length];
+      // Add methods from source.
+      int i = 0;
+      for (DexEncodedMethod method : sourceMethods) {
+        result[i] = method;
+        i++;
       }
+      // Add methods from target.
+      System.arraycopy(targetMethods, 0, result, i, targetMethods.length);
+      return result;
     }
 
     // Note that names returned by this function are not necessarily unique. Clients should
@@ -936,13 +929,6 @@
       return application.dexItemFactory.createString(freshName);
     }
 
-    private DexEncodedMethod resolveMethodConflict(
-        DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
-      assert false;
-      abortMerge = true;
-      return method;
-    }
-
     private DexEncodedMethod renameConstructor(
         DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
       assert method.isInstanceInitializer();
@@ -1008,23 +994,24 @@
       return method.toTypeSubstitutedMethod(newSignature);
     }
 
-    private DexEncodedField resolveFieldConflict(
+    private DexEncodedField renameFieldIfNeeded(
         DexEncodedField field, Predicate<DexField> availableFieldSignatures) {
       DexString oldName = field.field.name;
       DexType oldHolder = field.field.clazz;
 
-      DexField newSignature;
-      int count = 1;
-      do {
-        DexString newName = getFreshName(oldName.toSourceString(), count, oldHolder);
-        newSignature =
-            application.dexItemFactory.createField(target.type, field.field.type, newName);
-        count++;
-      } while (!availableFieldSignatures.test(newSignature));
+      DexField newSignature =
+          application.dexItemFactory.createField(target.type, field.field.type, oldName);
+      if (!availableFieldSignatures.test(newSignature)) {
+        int count = 1;
+        do {
+          DexString newName = getFreshName(oldName.toSourceString(), count, oldHolder);
+          newSignature =
+              application.dexItemFactory.createField(target.type, field.field.type, newName);
+          count++;
+        } while (!availableFieldSignatures.test(newSignature));
+      }
 
-      DexEncodedField result = field.toTypeSubstitutedField(newSignature);
-      deferredRenamings.map(field.field, result.field);
-      return result;
+      return field.toTypeSubstitutedField(newSignature);
     }
   }