Fix is-fresh-field-name check in tree fixer

Change-Id: I88fc1fa769fc7f7e8154922fe56d6450ef967e88
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 b751475..40b630c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -375,8 +375,10 @@
     assert verifyNoDuplicateFields();
   }
 
-  public void clearStaticFields() {
+  public DexEncodedField[] clearStaticFields() {
+    DexEncodedField[] previousFields = staticFields;
     setStaticFields(DexEncodedField.EMPTY_ARRAY);
+    return previousFields;
   }
 
   public void removeStaticField(int index) {
@@ -465,8 +467,10 @@
     assert verifyNoDuplicateFields();
   }
 
-  public void clearInstanceFields() {
+  public DexEncodedField[] clearInstanceFields() {
+    DexEncodedField[] previousFields = instanceFields;
     instanceFields = DexEncodedField.EMPTY_ARRAY;
+    return previousFields;
   }
 
   private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index fc4e3e9..db0178c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -312,7 +312,7 @@
 
   void mergeStaticFields() {
     group.forEachSource(classStaticFieldsMerger::addFields);
-    classStaticFieldsMerger.merge(group.getTarget());
+    classStaticFieldsMerger.merge();
     group.forEachSource(DexClass::clearStaticFields);
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
index 6183391..f94d407 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -14,27 +14,31 @@
 import java.util.Map;
 
 public class ClassStaticFieldsMerger {
-  private final Builder lensBuilder;
-  private final MergeGroup group;
-  private final Map<DexField, DexEncodedField> targetFields = new LinkedHashMap<>();
+
   private final DexItemFactory dexItemFactory;
+  private final MergeGroup group;
+  private final Builder lensBuilder;
+
+  private final Map<DexField, DexEncodedField> targetFields = new LinkedHashMap<>();
 
   public ClassStaticFieldsMerger(
       AppView<?> appView, HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
-    this.lensBuilder = lensBuilder;
-
-    this.group = group;
-    // Add mappings for all target fields.
-    group
-        .getTarget()
-        .staticFields()
-        .forEach(field -> targetFields.put(field.getReference(), field));
-
     this.dexItemFactory = appView.dexItemFactory();
+    this.group = group;
+    this.lensBuilder = lensBuilder;
   }
 
   private boolean isFresh(DexField fieldReference) {
-    return !targetFields.containsKey(fieldReference);
+    if (group.getTarget().lookupField(fieldReference) != null) {
+      // The target class has an instance or static field with the given reference.
+      return false;
+    }
+    if (targetFields.containsKey(fieldReference)) {
+      // We have already committed another static field from a source class in the merge group to
+      // the given field reference (but the field is not yet added to the target class).
+      return false;
+    }
+    return true;
   }
 
   private void addField(DexEncodedField field) {
@@ -53,10 +57,10 @@
   }
 
   public void addFields(DexProgramClass toMerge) {
-    toMerge.staticFields().forEach(this::addField);
+    toMerge.forEachStaticField(this::addField);
   }
 
-  public void merge(DexProgramClass clazz) {
-    clazz.setStaticFields(targetFields.values().toArray(DexEncodedField.EMPTY_ARRAY));
+  public void merge() {
+    group.getTarget().appendStaticFields(targetFields.values());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 20eb5a3..2b12d6d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -5,7 +5,9 @@
 package com.android.tools.r8.horizontalclassmerging;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.NestedGraphLens;
@@ -15,6 +17,7 @@
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Streams;
@@ -83,18 +86,21 @@
 
   public static class Builder {
 
-    private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
-        BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
-    private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> methodMap =
+    private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField>
+        newFieldSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
+    private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> methodMap =
         BidirectionalManyToOneHashMap.newIdentityHashMap();
-    private final BidirectionalManyToOneRepresentativeHashMap<DexMethod, DexMethod>
+    private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
         newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
     private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
         new IdentityHashMap<>();
 
-    private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> pendingMethodMapUpdates =
+    private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> pendingMethodMapUpdates =
         BidirectionalManyToOneHashMap.newIdentityHashMap();
-    private final BidirectionalManyToOneRepresentativeHashMap<DexMethod, DexMethod>
+    private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField>
+        pendingNewFieldSignatureUpdates =
+            BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
+    private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
         pendingNewMethodSignatureUpdates =
             BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
 
@@ -103,6 +109,7 @@
     HorizontalClassMergerGraphLens build(
         AppView<?> appView, HorizontallyMergedClasses mergedClasses) {
       assert pendingMethodMapUpdates.isEmpty();
+      assert pendingNewFieldSignatureUpdates.isEmpty();
       assert pendingNewMethodSignatureUpdates.isEmpty();
       assert newMethodSignatures.values().stream()
           .allMatch(
@@ -115,7 +122,7 @@
           appView,
           mergedClasses,
           methodExtraParameters,
-          fieldMap,
+          newFieldSignatures,
           methodMap.getForwardMap(),
           newMethodSignatures);
     }
@@ -126,7 +133,7 @@
     }
 
     void recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) {
-      fieldMap.put(oldFieldSignature, newFieldSignature);
+      newFieldSignatures.put(oldFieldSignature, newFieldSignature);
     }
 
     void recordNewFieldSignature(
@@ -135,26 +142,20 @@
         DexField representative) {
       assert Streams.stream(oldFieldSignatures)
           .anyMatch(oldFieldSignature -> oldFieldSignature != newFieldSignature);
-      assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue);
+      assert Streams.stream(oldFieldSignatures).noneMatch(newFieldSignatures::containsValue);
       assert Iterables.contains(oldFieldSignatures, representative);
       for (DexField oldFieldSignature : oldFieldSignatures) {
         recordNewFieldSignature(oldFieldSignature, newFieldSignature);
       }
-      fieldMap.setRepresentative(newFieldSignature, representative);
+      newFieldSignatures.setRepresentative(newFieldSignature, representative);
     }
 
     void fixupField(DexField oldFieldSignature, DexField newFieldSignature) {
-      DexField representative = fieldMap.removeRepresentativeFor(oldFieldSignature);
-      Set<DexField> originalFieldSignatures = fieldMap.removeValue(oldFieldSignature);
-      if (originalFieldSignatures.isEmpty()) {
-        fieldMap.put(oldFieldSignature, newFieldSignature);
-      } else if (originalFieldSignatures.size() == 1) {
-        fieldMap.put(originalFieldSignatures, newFieldSignature);
-      } else {
-        assert representative != null;
-        fieldMap.put(originalFieldSignatures, newFieldSignature);
-        fieldMap.setRepresentative(newFieldSignature, representative);
-      }
+      fixupOriginalMemberSignatures(
+          oldFieldSignature,
+          newFieldSignature,
+          newFieldSignatures,
+          pendingNewFieldSignatureUpdates);
     }
 
     void mapMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
@@ -195,7 +196,11 @@
 
     void fixupMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
       fixupMethodMap(oldMethodSignature, newMethodSignature);
-      fixupOriginalMethodSignatures(oldMethodSignature, newMethodSignature);
+      fixupOriginalMemberSignatures(
+          oldMethodSignature,
+          newMethodSignature,
+          newMethodSignatures,
+          pendingNewMethodSignatureUpdates);
     }
 
     private void fixupMethodMap(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
@@ -209,18 +214,22 @@
       }
     }
 
-    private void fixupOriginalMethodSignatures(
-        DexMethod oldMethodSignature, DexMethod newMethodSignature) {
-      Set<DexMethod> oldMethodSignatures = newMethodSignatures.getKeys(oldMethodSignature);
-      if (oldMethodSignatures.isEmpty()) {
-        pendingNewMethodSignatureUpdates.put(oldMethodSignature, newMethodSignature);
+    private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+        void fixupOriginalMemberSignatures(
+            R oldMemberSignature,
+            R newMemberSignature,
+            MutableBidirectionalManyToOneRepresentativeMap<R, R> newMemberSignatures,
+            MutableBidirectionalManyToOneRepresentativeMap<R, R> pendingNewMemberSignatureUpdates) {
+      Set<R> oldMemberSignatures = newMemberSignatures.getKeys(oldMemberSignature);
+      if (oldMemberSignatures.isEmpty()) {
+        pendingNewMemberSignatureUpdates.put(oldMemberSignature, newMemberSignature);
       } else {
-        for (DexMethod originalMethodSignature : oldMethodSignatures) {
-          pendingNewMethodSignatureUpdates.put(originalMethodSignature, newMethodSignature);
+        for (R originalMethodSignature : oldMemberSignatures) {
+          pendingNewMemberSignatureUpdates.put(originalMethodSignature, newMemberSignature);
         }
-        DexMethod representative = newMethodSignatures.getRepresentativeKey(oldMethodSignature);
+        R representative = newMemberSignatures.getRepresentativeKey(oldMemberSignature);
         if (representative != null) {
-          pendingNewMethodSignatureUpdates.setRepresentative(newMethodSignature, representative);
+          pendingNewMemberSignatureUpdates.setRepresentative(newMemberSignature, representative);
         }
       }
     }
@@ -231,16 +240,24 @@
       pendingMethodMapUpdates.forEachManyToOneMapping(methodMap::put);
       pendingMethodMapUpdates.clear();
 
-      // Commit pending original method signatures updates.
-      newMethodSignatures.removeAll(pendingNewMethodSignatureUpdates.keySet());
-      pendingNewMethodSignatureUpdates.forEachManyToOneMapping(
+      // Commit pending original field and method signatures updates.
+      commitPendingNewMemberSignatureUpdates(newFieldSignatures, pendingNewFieldSignatureUpdates);
+      commitPendingNewMemberSignatureUpdates(newMethodSignatures, pendingNewMethodSignatureUpdates);
+    }
+
+    private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+        void commitPendingNewMemberSignatureUpdates(
+            MutableBidirectionalManyToOneRepresentativeMap<R, R> newMemberSignatures,
+            MutableBidirectionalManyToOneRepresentativeMap<R, R> pendingNewMemberSignatureUpdates) {
+      newMemberSignatures.removeAll(pendingNewMemberSignatureUpdates.keySet());
+      pendingNewMemberSignatureUpdates.forEachManyToOneMapping(
           (keys, value, representative) -> {
-            newMethodSignatures.put(keys, value);
+            newMemberSignatures.put(keys, value);
             if (keys.size() > 1) {
-              newMethodSignatures.setRepresentative(value, representative);
+              newMemberSignatures.setRepresentative(value, representative);
             }
           });
-      pendingNewMethodSignatureUpdates.clear();
+      pendingNewMemberSignatureUpdates.clear();
     }
 
     /**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 442489d..b85eab3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass.FieldSetter;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -22,6 +21,7 @@
 import com.android.tools.r8.graph.TreeFixerBase;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AnnotationFixer;
+import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -168,8 +168,11 @@
         .getMethodCollection()
         .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, method));
 
-    fixupFields(clazz.staticFields(), clazz::setStaticField);
-    fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+    Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
+    DexEncodedField[] instanceFields = clazz.clearInstanceFields();
+    DexEncodedField[] staticFields = clazz.clearStaticFields();
+    clazz.setInstanceFields(fixupFields(instanceFields, newFieldReferences));
+    clazz.setStaticFields(fixupFields(staticFields, newFieldReferences));
 
     lensBuilder.commitPendingUpdates();
 
@@ -219,8 +222,13 @@
         .getMethodCollection()
         .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method));
     iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
-    fixupFields(iface.staticFields(), iface::setStaticField);
-    fixupFields(iface.instanceFields(), iface::setInstanceField);
+
+    assert !iface.hasInstanceFields();
+
+    Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
+    DexEncodedField[] staticFields = iface.clearStaticFields();
+    iface.setStaticFields(fixupFields(staticFields, newFieldReferences));
+
     lensBuilder.commitPendingUpdates();
   }
 
@@ -371,35 +379,40 @@
     return fixupProgramMethod(newMethodReference, method);
   }
 
-  private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
-    if (fields == null) {
-      return;
+  private DexEncodedField[] fixupFields(
+      DexEncodedField[] fields, Set<DexField> newFieldReferences) {
+    if (fields == null || ArrayUtils.isEmpty(fields)) {
+      return DexEncodedField.EMPTY_ARRAY;
     }
-    Set<DexField> existingFields = Sets.newIdentityHashSet();
 
-    for (int i = 0; i < fields.size(); i++) {
-      DexEncodedField oldField = fields.get(i);
+    DexEncodedField[] newFields = new DexEncodedField[fields.length];
+    for (int i = 0; i < fields.length; i++) {
+      DexEncodedField oldField = fields[i];
       DexField oldFieldReference = oldField.getReference();
       DexField newFieldReference = fixupFieldReference(oldFieldReference);
 
       // Rename the field if it already exists.
-      if (!existingFields.add(newFieldReference)) {
+      if (!newFieldReferences.add(newFieldReference)) {
         DexField template = newFieldReference;
         newFieldReference =
             dexItemFactory.createFreshMember(
                 tryName ->
                     Optional.of(template.withName(tryName, dexItemFactory))
-                        .filter(tryMethod -> !existingFields.contains(tryMethod)),
+                        .filter(tryMethod -> !newFieldReferences.contains(tryMethod)),
                 newFieldReference.name.toSourceString());
-        boolean added = existingFields.add(newFieldReference);
+        boolean added = newFieldReferences.add(newFieldReference);
         assert added;
       }
 
       if (newFieldReference != oldFieldReference) {
         lensBuilder.fixupField(oldFieldReference, newFieldReference);
-        setter.setField(i, oldField.toTypeSubstitutedField(newFieldReference));
+        newFields[i] = oldField.toTypeSubstitutedField(newFieldReference);
+      } else {
+        newFields[i] = oldField;
       }
     }
+
+    return newFields;
   }
 
   @Override