Refactor handling of reflective member lookup

Change-Id: Id3b7748165fa62af987a5042376b86c6d05d63b0
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 5fc4fd2..26c040a 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
@@ -17,7 +17,6 @@
 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;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -32,7 +31,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
-import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringTypeLookupResult;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.InstantiationReason;
 import com.android.tools.r8.shaking.KeepFieldInfo;
@@ -145,12 +143,17 @@
     DexType holder = invokedMethod.getHolderType();
     if (holder.isIdenticalTo(factory.classType)) {
       // java.lang.Class
-      if (invokedMethod.isIdenticalTo(factory.classMethods.newInstance)) {
-        handleJavaLangClassNewInstance(method, invoke);
-      } else if (factory.classMethods.isReflectiveClassLookup(invokedMethod)) {
+      if (invokedMethod.isIdenticalTo(factory.classMethods.forName)
+          || invokedMethod.isIdenticalTo(factory.classMethods.forName3)) {
         handleJavaLangClassForName(method, invoke);
-      } else if (factory.classMethods.isReflectiveMemberLookup(invokedMethod)) {
-        handleJavaLangClassGetMember(method, invoke);
+      } else if (invokedMethod.isIdenticalTo(factory.classMethods.getField)
+          || invokedMethod.isIdenticalTo(factory.classMethods.getDeclaredField)) {
+        handleJavaLangClassGetField(method, invoke);
+      } else if (invokedMethod.isIdenticalTo(factory.classMethods.getMethod)
+          || invokedMethod.isIdenticalTo(factory.classMethods.getDeclaredMethod)) {
+        handleJavaLangClassGetMethod(method, invoke);
+      } else if (invokedMethod.isIdenticalTo(factory.classMethods.newInstance)) {
+        handleJavaLangClassNewInstance(method, invoke);
       }
     } else if (holder.isIdenticalTo(factory.constructorType)) {
       // java.lang.reflect.Constructor
@@ -212,6 +215,80 @@
     }
   }
 
+  private void handleJavaLangClassGetField(ProgramMethod method, InvokeMethod invoke) {
+    IdentifierNameStringLookupResult<?> identifierLookupResult =
+        identifyIdentifier(invoke, appView, method);
+    if (identifierLookupResult == null) {
+      return;
+    }
+
+    DexField field = identifierLookupResult.getReference().asDexField();
+    assert field != null;
+
+    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 handleJavaLangClassGetMethod(ProgramMethod method, InvokeMethod invoke) {
+    IdentifierNameStringLookupResult<?> identifierLookupResult =
+        identifyIdentifier(invoke, appView, method);
+    if (identifierLookupResult == null) {
+      return;
+    }
+
+    DexMethod targetedMethodReference = identifierLookupResult.getReference().asDexMethod();
+    assert targetedMethodReference != null;
+
+    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());
+  }
+
   /** Handles reflective uses of {@link Class#newInstance()}. */
   private void handleJavaLangClassNewInstance(ProgramMethod method, InvokeMethod invoke) {
     if (!invoke.isInvokeVirtual()) {
@@ -450,107 +527,4 @@
       eventConsumer.onJavaUtilServiceLoaderLoad(serviceClass, implementationClasses, context);
     }
   }
-
-  private void handleJavaLangClassGetMember(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() || 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());
-    }
-  }
 }