Merge "Strengthen no duplicate methods check"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6c3e47e..011c026 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -616,15 +616,18 @@
         new DiscardedChecker(rootSet, application, options).run();
       }
 
-      timing.begin("Minification");
-      // If we did not have keep rules, everything will be marked as keep, so no minification
-      // will happen. Just avoid the overhead.
-      NamingLens namingLens =
-          options.enableMinification
-              ? new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
-                  .run(timing)
-              : NamingLens.getIdentityLens();
-      timing.end();
+      // Perform minification.
+      NamingLens namingLens;
+      if (options.enableMinification) {
+        timing.begin("Minification");
+        namingLens =
+            new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
+                .run(timing);
+        timing.end();
+        assert namingLens.verifyNoCollisions(application.classes(), options.itemFactory);
+      } else {
+        namingLens = NamingLens.getIdentityLens();
+      }
 
       ProguardMapSupplier proguardMapSupplier;
 
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 28aabea..5e65aa3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -38,22 +38,17 @@
   public DexTypeList interfaces;
   public DexString sourceFile;
 
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedField[] staticFields;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedField[] instanceFields;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedMethod[] directMethods;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedMethod[] virtualMethods;
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedField[] staticFields = NO_FIELDS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedField[] instanceFields = NO_FIELDS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedMethod[] directMethods = NO_METHODS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedMethod[] virtualMethods = NO_METHODS;
 
   /** Enclosing context of this class if it is an inner class, null otherwise. */
   private EnclosingMethodAttribute enclosingMethod;
@@ -146,7 +141,8 @@
     System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
     newMethods[directMethods.length] = method;
     directMethods = newMethods;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
@@ -158,7 +154,8 @@
       i++;
     }
     directMethods = newMethods;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolders(methods);
+    assert verifyNoDuplicateMethods();
   }
 
   public void removeDirectMethod(int index) {
@@ -170,12 +167,14 @@
 
   public void setDirectMethod(int index, DexEncodedMethod method) {
     directMethods[index] = method;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void setDirectMethods(DexEncodedMethod[] values) {
     directMethods = MoreObjects.firstNonNull(values, NO_METHODS);
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolders(directMethods());
+    assert verifyNoDuplicateMethods();
   }
 
   public List<DexEncodedMethod> virtualMethods() {
@@ -191,7 +190,8 @@
     System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
     newMethods[virtualMethods.length] = method;
     virtualMethods = newMethods;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
@@ -203,7 +203,8 @@
       i++;
     }
     virtualMethods = newMethods;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolders(methods);
+    assert verifyNoDuplicateMethods();
   }
 
   public void removeVirtualMethod(int index) {
@@ -216,22 +217,39 @@
 
   public void setVirtualMethod(int index, DexEncodedMethod method) {
     virtualMethods[index] = method;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void setVirtualMethods(DexEncodedMethod[] values) {
     virtualMethods = MoreObjects.firstNonNull(values, NO_METHODS);
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolders(virtualMethods());
+    assert verifyNoDuplicateMethods();
   }
 
-  private boolean verifyNoDuplicateMethods(DexEncodedMethod[] methods) {
+  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;
+  }
+
+  private boolean verifyNoDuplicateMethods() {
     Set<DexMethod> unique = Sets.newIdentityHashSet();
-    Arrays.stream(methods)
-        .forEach(
-            method -> {
-              boolean changed = unique.add(method.method);
-              assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
-            });
+    for (DexEncodedMethod method : methods()) {
+      boolean changed = unique.add(method.method);
+      assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
+    }
     return true;
   }
 
@@ -651,6 +669,8 @@
   public boolean isValid() {
     assert !isInterface()
         || Arrays.stream(virtualMethods).noneMatch(method -> method.accessFlags.isFinal());
+    assert verifyCorrectnessOfMethodHolders(methods());
+    assert verifyNoDuplicateMethods();
     return true;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index cee867e..aecae1e 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -7,10 +7,14 @@
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 
 import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -19,7 +23,10 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -70,6 +77,40 @@
     return lookupName(reference.asDexField());
   }
 
+  public final DexField lookupField(DexField field, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createField(
+        lookupType(field.clazz, dexItemFactory),
+        lookupType(field.type, dexItemFactory),
+        lookupName(field));
+  }
+
+  public final DexMethod lookupMethod(DexMethod method, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createMethod(
+        lookupType(method.holder, dexItemFactory),
+        lookupProto(method.proto, dexItemFactory),
+        lookupName(method));
+  }
+
+  private DexProto lookupProto(DexProto proto, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createProto(
+        lookupType(proto.returnType, dexItemFactory),
+        Arrays.stream(proto.parameters.values)
+            .map(type -> lookupType(type, dexItemFactory))
+            .toArray(DexType[]::new));
+  }
+
+  public final DexType lookupType(DexType type, DexItemFactory dexItemFactory) {
+    if (type.isPrimitiveType() || type.isVoidType()) {
+      return type;
+    }
+    if (type.isArrayType()) {
+      DexType newBaseType = lookupType(type.toBaseType(dexItemFactory), dexItemFactory);
+      return type.replaceBaseType(newBaseType, dexItemFactory);
+    }
+    assert type.isClassType();
+    return dexItemFactory.createType(lookupDescriptor(type));
+  }
+
   public static NamingLens getIdentityLens() {
     return new IdentityLens();
   }
@@ -97,6 +138,34 @@
    */
   public abstract boolean checkTargetCanBeTranslated(DexMethod item);
 
+  public final boolean verifyNoCollisions(
+      Iterable<DexProgramClass> classes, DexItemFactory dexItemFactory) {
+    Set<DexReference> references = Sets.newIdentityHashSet();
+    for (DexProgramClass clazz : classes) {
+      {
+        DexType newType = lookupType(clazz.type, dexItemFactory);
+        boolean referencesChanged = references.add(newType);
+        assert referencesChanged
+            : "Duplicate definition of type `" + newType.toSourceString() + "`";
+      }
+
+      for (DexEncodedField field : clazz.fields()) {
+        DexField newField = lookupField(field.field, dexItemFactory);
+        boolean referencesChanged = references.add(newField);
+        assert referencesChanged
+            : "Duplicate definition of field `" + newField.toSourceString() + "`";
+      }
+
+      for (DexEncodedMethod method : clazz.methods()) {
+        DexMethod newMethod = lookupMethod(method.method, dexItemFactory);
+        boolean referencesChanged = references.add(newMethod);
+        assert referencesChanged
+            : "Duplicate definition of method `" + newMethod.toSourceString() + "`";
+      }
+    }
+    return true;
+  }
+
   private static class IdentityLens extends NamingLens {
 
     private IdentityLens() {