Rewrite invoke structures in Enqueuer

This CL rewrites the invoke sets in the Enqueuer from having type `Map<DexType, Set<TargetWithContext<DexMethod>>>` to having type `Map<DexMethod, Set<DexEncodedMethod>>`. This way no post-processing of the data is needed for creating AppInfoWithLiveness, except sorting.

Change-Id: I803f5666ab2fa564f29539a18f44a6341a17fcd2
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 04b9b53..717c7ce 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
 import static com.android.tools.r8.shaking.EnqueuerUtils.extractProgramFieldDefinitions;
+import static com.android.tools.r8.shaking.EnqueuerUtils.toImmutableSortedMap;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.IndexedItemCollection;
@@ -116,16 +117,11 @@
   private RootSet rootSet;
   private ProguardClassFilter dontWarnPatterns;
 
-  private final Map<DexType, Set<TargetWithContext<DexMethod>>> virtualInvokes =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexMethod>>> interfaceInvokes =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexMethod>>> superInvokes =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexMethod>>> directInvokes =
-      Maps.newIdentityHashMap();
-  private final Map<DexType, Set<TargetWithContext<DexMethod>>> staticInvokes =
-      Maps.newIdentityHashMap();
+  private final Map<DexMethod, Set<DexEncodedMethod>> virtualInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, Set<DexEncodedMethod>> interfaceInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, Set<DexEncodedMethod>> superInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, Set<DexEncodedMethod>> directInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, Set<DexEncodedMethod>> staticInvokes = new IdentityHashMap<>();
   private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
       Maps.newIdentityHashMap();
   private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
@@ -411,36 +407,27 @@
   // traversals.
   //
 
-  private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTarget(
-      Map<DexType, Set<T>> seen, T item) {
-    assert item.isDexField() || item.isDexMethod();
-    DexType itemHolder =
-        item.isDexField()
-            ? item.asDexField().holder
-            : item.asDexMethod().holder;
-    DexType holder = itemHolder.toBaseType(appView.dexItemFactory());
-    if (!holder.isClassType()) {
-      return false;
+  private boolean registerFieldWithTargetAndContext(
+      Map<DexType, Set<TargetWithContext<DexField>>> seen,
+      DexField field,
+      DexEncodedMethod context) {
+    DexType baseHolder = field.holder.toBaseType(appView.dexItemFactory());
+    if (baseHolder.isClassType()) {
+      markTypeAsLive(baseHolder);
+      return seen.computeIfAbsent(field.holder, ignore -> new HashSet<>())
+          .add(new TargetWithContext<>(field, context));
     }
-    markTypeAsLive(holder);
-    return seen.computeIfAbsent(itemHolder, (ignore) -> Sets.newIdentityHashSet()).add(item);
+    return false;
   }
 
-  private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTargetAndContext(
-      Map<DexType, Set<TargetWithContext<T>>> seen, T item, DexEncodedMethod context) {
-    assert item.isDexField() || item.isDexMethod();
-    DexType itemHolder =
-        item.isDexField()
-            ? item.asDexField().holder
-            : item.asDexMethod().holder;
-    DexType holder = itemHolder.toBaseType(appView.dexItemFactory());
-    if (!holder.isClassType()) {
-      return false;
+  private boolean registerMethodWithTargetAndContext(
+      Map<DexMethod, Set<DexEncodedMethod>> seen, DexMethod method, DexEncodedMethod context) {
+    DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory());
+    if (baseHolder.isClassType()) {
+      markTypeAsLive(baseHolder);
+      return seen.computeIfAbsent(method, ignore -> Sets.newIdentityHashSet()).add(context);
     }
-    markTypeAsLive(holder);
-    return seen
-        .computeIfAbsent(itemHolder, (ignore) -> new HashSet<>())
-        .add(new TargetWithContext<>(item, context));
+    return false;
   }
 
   private class UseRegistry extends com.android.tools.r8.graph.UseRegistry {
@@ -467,7 +454,7 @@
         // Revisit the current method to implicitly add -keep rule for items with reflective access.
         pendingReflectiveUses.add(currentMethod);
       }
-      if (!registerItemWithTargetAndContext(virtualInvokes, method, currentMethod)) {
+      if (!registerMethodWithTargetAndContext(virtualInvokes, method, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -483,7 +470,7 @@
     }
 
     boolean registerInvokeDirect(DexMethod method, KeepReason keepReason) {
-      if (!registerItemWithTargetAndContext(directInvokes, method, currentMethod)) {
+      if (!registerMethodWithTargetAndContext(directInvokes, method, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -518,7 +505,7 @@
       if (method == dexItemFactory.proxyMethods.newProxyInstance) {
         pendingReflectiveUses.add(currentMethod);
       }
-      if (!registerItemWithTargetAndContext(staticInvokes, method, currentMethod)) {
+      if (!registerMethodWithTargetAndContext(staticInvokes, method, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -534,7 +521,7 @@
     }
 
     boolean registerInvokeInterface(DexMethod method, KeepReason keepReason) {
-      if (!registerItemWithTargetAndContext(interfaceInvokes, method, currentMethod)) {
+      if (!registerMethodWithTargetAndContext(interfaceInvokes, method, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -549,7 +536,7 @@
       // We have to revisit super invokes based on the context they are found in. The same
       // method descriptor will hit different targets, depending on the context it is used in.
       DexMethod actualTarget = getInvokeSuperTarget(method, currentMethod);
-      if (!registerItemWithTargetAndContext(superInvokes, method, currentMethod)) {
+      if (!registerMethodWithTargetAndContext(superInvokes, method, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -561,7 +548,7 @@
 
     @Override
     public boolean registerInstanceFieldWrite(DexField field) {
-      if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
+      if (!registerFieldWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -574,7 +561,7 @@
 
     @Override
     public boolean registerInstanceFieldRead(DexField field) {
-      if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
+      if (!registerFieldWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -596,7 +583,7 @@
 
     @Override
     public boolean registerStaticFieldRead(DexField field) {
-      if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
+      if (!registerFieldWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -608,7 +595,7 @@
 
     @Override
     public boolean registerStaticFieldWrite(DexField field) {
-      if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
+      if (!registerFieldWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
         return false;
       }
       if (Log.ENABLED) {
@@ -1586,11 +1573,12 @@
             instanceFieldWrites,
             staticFieldReads,
             staticFieldWrites,
-            enqueuer.collectDescriptors(enqueuer.virtualInvokes),
-            enqueuer.collectDescriptors(enqueuer.interfaceInvokes),
-            enqueuer.collectDescriptors(enqueuer.superInvokes),
-            enqueuer.collectDescriptors(enqueuer.directInvokes),
-            enqueuer.collectDescriptors(enqueuer.staticInvokes),
+            // TODO(b/132593519): Do we require these sets to be sorted for determinism?
+            toImmutableSortedMap(enqueuer.virtualInvokes, PresortedComparable::slowCompare),
+            toImmutableSortedMap(enqueuer.interfaceInvokes, PresortedComparable::slowCompare),
+            toImmutableSortedMap(enqueuer.superInvokes, PresortedComparable::slowCompare),
+            toImmutableSortedMap(enqueuer.directInvokes, PresortedComparable::slowCompare),
+            toImmutableSortedMap(enqueuer.staticInvokes, PresortedComparable::slowCompare),
             enqueuer.callSites,
             ImmutableSortedSet.copyOf(DexMethod::slowCompareTo, enqueuer.brokenSuperInvokes),
             enqueuer.pinnedItems,
@@ -2017,11 +2005,11 @@
         markFieldAsKept(encodedField, KeepReason.reflectiveUseIn(method));
         // Fields accessed by reflection is marked as both read and written.
         if (encodedField.isStatic()) {
-          registerItemWithTargetAndContext(staticFieldsRead, encodedField.field, method);
-          registerItemWithTargetAndContext(staticFieldsWritten, encodedField.field, method);
+          registerFieldWithTargetAndContext(staticFieldsRead, encodedField.field, method);
+          registerFieldWithTargetAndContext(staticFieldsWritten, encodedField.field, method);
         } else {
-          registerItemWithTargetAndContext(instanceFieldsRead, encodedField.field, method);
-          registerItemWithTargetAndContext(instanceFieldsWritten, encodedField.field, method);
+          registerFieldWithTargetAndContext(instanceFieldsRead, encodedField.field, method);
+          registerFieldWithTargetAndContext(instanceFieldsWritten, encodedField.field, method);
         }
       }
     } else {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUtils.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUtils.java
index 187e0a1..6cff285 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUtils.java
@@ -8,7 +8,10 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.PresortedComparable;
+import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
+import java.util.Comparator;
+import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.function.Predicate;
@@ -48,4 +51,11 @@
     }
     return builder.build();
   }
+
+  static <T, U> ImmutableSortedMap<T, U> toImmutableSortedMap(
+      Map<T, U> map, Comparator<T> comparator) {
+    ImmutableSortedMap.Builder<T, U> builder = new ImmutableSortedMap.Builder<>(comparator);
+    map.forEach(builder::put);
+    return builder.build();
+  }
 }