Move some reflective identification handling from Enqueuer

This also refactors the reflective identification into smaller methods.

Change-Id: I7f24b4870b46cb50d72daf9868fce785a052f306
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 6fab82f..4d8c7c5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -358,6 +358,14 @@
       createString("Ljava/util/concurrent/atomic/AtomicLongFieldUpdater;");
   public final DexString referenceFieldUpdaterDescriptor =
       createString("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
+
+  public final DexType javaUtilConcurrentAtomicAtomicIntegerFieldUpdater =
+      createType(intFieldUpdaterDescriptor);
+  public final DexType javaUtilConcurrentAtomicAtomicLongFieldUpdater =
+      createType(longFieldUpdaterDescriptor);
+  public final DexType javaUtilConcurrentAtomicAtomicReferenceFieldUpdater =
+      createType(referenceFieldUpdaterDescriptor);
+
   public final DexString newUpdaterName = createString("newUpdater");
 
   public final DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
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 6e60a93..4dc8598 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -51,7 +51,6 @@
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexItemFactory.ClassMethods;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMember;
@@ -262,8 +261,6 @@
   private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
   private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>();
 
-  private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
-
   private List<KeepDeclaration> keepDeclarations = Collections.emptyList();
   private ApplicableRulesEvaluator applicableRules = ApplicableRulesEvaluator.empty();
 
@@ -1602,19 +1599,7 @@
     if (registry != null && !registry.markInvokeStaticAsSeen(invokedMethod)) {
       return;
     }
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
-        || dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
-      // Implicitly add -identifiernamestring rule for the Java reflection in use.
-      identifierNameStrings.add(invokedMethod);
-      // Revisit the current method to implicitly add -keep rule for items with reflective access.
-      reflectiveIdentificationAnalysis.enqueue(context);
-    } else if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
-      reflectiveIdentificationAnalysis.enqueue(context);
-    } else if (dexItemFactory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
-      // Handling of application services.
-      reflectiveIdentificationAnalysis.enqueue(context);
-    }
+    reflectiveIdentificationAnalysis.scanInvoke(invokedMethod, context);
     markTypeAsLive(invokedMethod.getHolderType(), context);
     MethodResolutionResult resolutionResult =
         handleInvokeOfStaticTarget(invokedMethod, context, reason);
@@ -1651,16 +1636,7 @@
     if (registry != null && !registry.markInvokeVirtualAsSeen(invokedMethod)) {
       return;
     }
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    if (invokedMethod.isIdenticalTo(dexItemFactory.classMethods.newInstance)
-        || invokedMethod.isIdenticalTo(dexItemFactory.constructorMethods.newInstance)) {
-      reflectiveIdentificationAnalysis.enqueue(context);
-    } else if (dexItemFactory.classMethods.isReflectiveMemberLookup(invokedMethod)) {
-      // Implicitly add -identifiernamestring rule for the Java reflection in use.
-      identifierNameStrings.add(invokedMethod);
-      // Revisit the current method to implicitly add -keep rule for items with reflective access.
-      reflectiveIdentificationAnalysis.enqueue(context);
-    }
+    reflectiveIdentificationAnalysis.scanInvoke(invokedMethod, context);
     markTypeAsLive(invokedMethod.getHolderType(), context);
     MethodResolutionResult resolutionResult =
         markVirtualMethodAsReachable(invokedMethod, false, context, reason);
@@ -4607,7 +4583,9 @@
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
             amendWithCompanionMethods(rootSet.reprocess),
             rootSet.alwaysClassInline,
-            joinIdentifierNameStrings(rootSet.identifierNameStrings, identifierNameStrings),
+            joinIdentifierNameStrings(
+                rootSet.identifierNameStrings,
+                reflectiveIdentificationAnalysis.getIdentifierNameStrings()),
             emptySet(),
             Collections.emptyMap(),
             lockCandidates,
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
index f52fba7..0d4e6b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationAnalysis.java
@@ -12,6 +12,8 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexItemFactory.ClassMethods;
+import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
@@ -40,9 +42,11 @@
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.android.tools.r8.utils.timing.Timing;
+import com.google.common.collect.Sets;
 import java.lang.reflect.InvocationHandler;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 public class EnqueuerReflectiveIdentificationAnalysis {
 
@@ -51,6 +55,7 @@
   private final Enqueuer enqueuer;
   private final InternalOptions options;
 
+  private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
   private final ProgramMethodSet worklist = ProgramMethodSet.create();
 
   public EnqueuerReflectiveIdentificationAnalysis(
@@ -61,6 +66,51 @@
     this.options = appView.options();
   }
 
+  public Set<DexMember<?, ?>> getIdentifierNameStrings() {
+    return identifierNameStrings;
+  }
+
+  public void scanInvoke(DexMethod invokedMethod, ProgramMethod method) {
+    ClassMethods classMethods = factory.classMethods;
+    DexType holder = invokedMethod.getHolderType();
+    if (holder.isIdenticalTo(factory.classType)) {
+      // java.lang.Class
+      if (invokedMethod.isIdenticalTo(classMethods.newInstance)) {
+        enqueue(method);
+      } else if (classMethods.isReflectiveClassLookup(invokedMethod)
+          || classMethods.isReflectiveMemberLookup(invokedMethod)) {
+        // Implicitly add -identifiernamestring rule for the Java reflection in use.
+        identifierNameStrings.add(invokedMethod);
+        enqueue(method);
+      }
+    } else if (holder.isIdenticalTo(factory.constructorType)) {
+      // java.lang.reflect.Constructor
+      if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
+        enqueue(method);
+      }
+    } else if (holder.isIdenticalTo(factory.proxyType)) {
+      // java.lang.reflect.Proxy
+      if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+        enqueue(method);
+      }
+    } else if (holder.isIdenticalTo(factory.serviceLoaderType)) {
+      // java.util.ServiceLoader
+      if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
+        enqueue(method);
+      }
+    } else if (holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicIntegerFieldUpdater)
+        || holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicLongFieldUpdater)
+        || holder.isIdenticalTo(factory.javaUtilConcurrentAtomicAtomicReferenceFieldUpdater)) {
+      // java.util.concurrent.atomic.AtomicIntegerFieldUpdater
+      // java.util.concurrent.atomic.AtomicLongFieldUpdater
+      // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+      if (factory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
+        identifierNameStrings.add(invokedMethod);
+        enqueue(method);
+      }
+    }
+  }
+
   public void enqueue(ProgramMethod method) {
     worklist.add(method);
   }
@@ -89,117 +139,16 @@
     DexMethod invokedMethod = invoke.getInvokedMethod();
     if (invokedMethod.isIdenticalTo(factory.classMethods.newInstance)) {
       handleJavaLangClassNewInstance(method, invoke);
-      return;
-    }
-    if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
+    } else if (invokedMethod.isIdenticalTo(factory.constructorMethods.newInstance)) {
       handleJavaLangReflectConstructorNewInstance(method, invoke);
-      return;
-    }
-    if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+    } else if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
       handleJavaLangReflectProxyNewProxyInstance(method, invoke);
-      return;
-    }
-    if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
+    } else if (factory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
       handleServiceLoaderInvocation(method, invoke);
-      return;
-    }
-    if (enqueuer.getAnalyses().handleReflectiveInvoke(method, invoke)) {
-      return;
-    }
-    if (!isReflectionMethod(factory, invokedMethod)) {
-      return;
-    }
-    IdentifierNameStringLookupResult<?> identifierLookupResult =
-        identifyIdentifier(invoke, appView, method);
-    if (identifierLookupResult == null) {
-      return;
-    }
-    DexReference referencedItem = identifierLookupResult.getReference();
-    if (referencedItem.isDexType()) {
-      DexType referencedType = referencedItem.asDexType();
-      if (!referencedType.isClassType()
-          || appView.allMergedClasses().isMergeSource(referencedType)) {
-        return;
-      }
-      assert identifierLookupResult.isTypeResult();
-      IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
-          identifierLookupResult.asTypeResult();
-      DexProgramClass clazz =
-          enqueuer.getProgramClassOrNullFromReflectiveAccess(referencedType, method);
-      if (clazz == null) {
-        return;
-      }
-      enqueuer.markTypeAsLive(clazz, KeepReason.reflectiveUseIn(method));
-      if (clazz.canBeInstantiatedByNewInstance()
-          && identifierTypeLookupResult.isTypeCompatInstantiatedFromUse(options)) {
-        enqueuer.markClassAsInstantiatedWithCompatRule(
-            clazz, () -> KeepReason.reflectiveUseIn(method));
-      } else if (identifierTypeLookupResult.isTypeInitializedFromUse()) {
-        enqueuer.markDirectAndIndirectClassInitializersAsLive(clazz);
-      }
-      // To ensure we are not moving the class because we cannot prune it when there is a reflective
-      // use of it.
-      if (enqueuer.getKeepInfo().getClassInfo(clazz).isShrinkingAllowed(options)) {
-        enqueuer
-            .getKeepInfo()
-            .joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
-      }
-    } else if (referencedItem.isDexField()) {
-      DexField field = referencedItem.asDexField();
-      DexProgramClass clazz =
-          enqueuer.getProgramClassOrNullFromReflectiveAccess(field.holder, method);
-      if (clazz == null) {
-        return;
-      }
-      DexEncodedField encodedField = clazz.lookupField(field);
-      if (encodedField == null) {
-        return;
-      }
-      // Normally, we generate a -keepclassmembers rule for the field, such that the field is only
-      // kept if it is a static field, or if the holder or one of its subtypes are instantiated.
-      // However, if the invoked method is a field updater, then we always need to keep instance
-      // fields since the creation of a field updater throws a NoSuchFieldException if the field
-      // is not present.
-      boolean keepClass =
-          !encodedField.isStatic()
-              && factory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
-      if (keepClass) {
-        enqueuer
-            .getWorklist()
-            .enqueueMarkInstantiatedAction(
-                clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
-      }
-      if (enqueuer.getKeepInfo().getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
-        ProgramField programField = new ProgramField(clazz, encodedField);
-        enqueuer.applyMinimumKeepInfoWhenLive(
-            programField,
-            KeepFieldInfo.newEmptyJoiner()
-                .disallowOptimization()
-                .disallowShrinking()
-                .addReason(KeepReason.reflectiveUseIn(method)));
-      }
-    } else {
-      assert referencedItem.isDexMethod();
-      DexMethod targetedMethodReference = referencedItem.asDexMethod();
-      DexProgramClass clazz =
-          enqueuer.getProgramClassOrNullFromReflectiveAccess(
-              targetedMethodReference.holder, method);
-      if (clazz == null) {
-        return;
-      }
-      ProgramMethod targetedMethod = clazz.lookupProgramMethod(targetedMethodReference);
-      if (targetedMethod == null) {
-        return;
-      }
-      KeepReason reason = KeepReason.reflectiveUseIn(method);
-      if (targetedMethod.getDefinition().belongsToDirectPool()) {
-        enqueuer.markMethodAsTargeted(targetedMethod, reason);
-        enqueuer.markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason);
-      } else {
-        enqueuer.markVirtualMethodAsLive(targetedMethod, reason);
-      }
-      enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
-          targetedMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+    } else if (enqueuer.getAnalyses().handleReflectiveInvoke(method, invoke)) {
+      // Intentionally empty.
+    } else if (isReflectionMethod(factory, invokedMethod)) {
+      handleReflectiveLookup(method, invoke);
     }
   }
 
@@ -493,4 +442,107 @@
       }
     }
   }
+
+  private void handleReflectiveLookup(ProgramMethod method, InvokeMethod invoke) {
+    IdentifierNameStringLookupResult<?> identifierLookupResult =
+        identifyIdentifier(invoke, appView, method);
+    if (identifierLookupResult != null) {
+      DexReference referencedItem = identifierLookupResult.getReference();
+      referencedItem.accept(
+          referencedType ->
+              handleReflectiveTypeLookup(method, referencedType, identifierLookupResult),
+          referencedField -> handleReflectiveFieldLookup(method, invoke, referencedField),
+          referencedMethod -> handleReflectiveMethodLookup(method, referencedMethod));
+    }
+  }
+
+  private void handleReflectiveFieldLookup(
+      ProgramMethod method, InvokeMethod invoke, DexField field) {
+    DexProgramClass clazz =
+        enqueuer.getProgramClassOrNullFromReflectiveAccess(field.holder, method);
+    if (clazz == null) {
+      return;
+    }
+    DexEncodedField encodedField = clazz.lookupField(field);
+    if (encodedField == null) {
+      return;
+    }
+    // Normally, we generate a -keepclassmembers rule for the field, such that the field is only
+    // kept if it is a static field, or if the holder or one of its subtypes are instantiated.
+    // However, if the invoked method is a field updater, then we always need to keep instance
+    // fields since the creation of a field updater throws a NoSuchFieldException if the field
+    // is not present.
+    boolean keepClass =
+        !encodedField.isStatic()
+            && factory.atomicFieldUpdaterMethods.isFieldUpdater(invoke.getInvokedMethod());
+    if (keepClass) {
+      enqueuer
+          .getWorklist()
+          .enqueueMarkInstantiatedAction(
+              clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
+    }
+    if (enqueuer.getKeepInfo().getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
+      ProgramField programField = new ProgramField(clazz, encodedField);
+      enqueuer.applyMinimumKeepInfoWhenLive(
+          programField,
+          KeepFieldInfo.newEmptyJoiner()
+              .disallowOptimization()
+              .disallowShrinking()
+              .addReason(KeepReason.reflectiveUseIn(method)));
+    }
+  }
+
+  private void handleReflectiveMethodLookup(
+      ProgramMethod method, DexMethod targetedMethodReference) {
+    DexProgramClass clazz =
+        enqueuer.getProgramClassOrNullFromReflectiveAccess(targetedMethodReference.holder, method);
+    if (clazz == null) {
+      return;
+    }
+    ProgramMethod targetedMethod = clazz.lookupProgramMethod(targetedMethodReference);
+    if (targetedMethod == null) {
+      return;
+    }
+    KeepReason reason = KeepReason.reflectiveUseIn(method);
+    if (targetedMethod.getDefinition().belongsToDirectPool()) {
+      enqueuer.markMethodAsTargeted(targetedMethod, reason);
+      enqueuer.markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason);
+    } else {
+      enqueuer.markVirtualMethodAsLive(targetedMethod, reason);
+    }
+    enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
+        targetedMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+  }
+
+  private void handleReflectiveTypeLookup(
+      ProgramMethod method,
+      DexType referencedType,
+      IdentifierNameStringLookupResult<?> identifierLookupResult) {
+    if (!referencedType.isClassType()) {
+      return;
+    }
+    assert identifierLookupResult.isTypeResult();
+    IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
+        identifierLookupResult.asTypeResult();
+    DexProgramClass clazz =
+        enqueuer.getProgramClassOrNullFromReflectiveAccess(referencedType, method);
+    if (clazz == null) {
+      return;
+    }
+    enqueuer.markTypeAsLive(clazz, KeepReason.reflectiveUseIn(method));
+    if (clazz.canBeInstantiatedByNewInstance()
+        && identifierTypeLookupResult.isTypeCompatInstantiatedFromUse(options)) {
+      enqueuer.markClassAsInstantiatedWithCompatRule(
+          clazz, () -> KeepReason.reflectiveUseIn(method));
+    } else if (identifierTypeLookupResult.isTypeInitializedFromUse()) {
+      enqueuer.markDirectAndIndirectClassInitializersAsLive(clazz);
+    }
+    // To ensure we are not moving the class because we cannot prune it when there is a reflective
+    // use of it.
+    if (enqueuer.getKeepInfo().getClassInfo(clazz).isShrinkingAllowed(options)) {
+      enqueuer
+          .getKeepInfo()
+          .joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
+    }
+  }
 }