Migrate getClass-to-ConstClass conversion.

This is a mechanical migration to place related code together.

Change-Id: Id49dadda7bd92af5b4656569b5296f41dd48297b
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 38842da..bdee0ac 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -56,6 +56,7 @@
 import com.android.tools.r8.ir.optimize.Outliner;
 import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
 import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
+import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
 import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
 import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
@@ -904,7 +905,7 @@
 
     if (appInfo.hasLiveness()) {
       // Reflection optimization 1. getClass() -> const-class
-      codeRewriter.rewriteGetClass(code);
+      ReflectionOptimizer.rewriteGetClass(appInfo.withLiveness(), code);
     }
 
     if (!isDebugMode) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 785041c..18c31ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -47,7 +47,6 @@
 import com.android.tools.r8.ir.code.CheckCast;
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
-import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
@@ -1948,67 +1947,6 @@
     return converter.definitionFor(type);
   }
 
-  // Rewrite getClass() call to const-class if the type of the given instance is effectively final.
-  public void rewriteGetClass(IRCode code) {
-    InstructionIterator it = code.instructionIterator();
-    while (it.hasNext()) {
-      Instruction current = it.next();
-      // Conservatively bail out if the containing block has catch handlers.
-      // TODO(b/118509730): unless join of all catch types is ClassNotFoundException ?
-      if (current.getBlock().hasCatchHandlers()) {
-        continue;
-      }
-      if (!current.isInvokeVirtual()) {
-        continue;
-      }
-      InvokeVirtual invoke = current.asInvokeVirtual();
-      DexMethod invokedMethod = invoke.getInvokedMethod();
-      // Class<?> Object#getClass() is final and cannot be overridden.
-      if (invokedMethod != appInfo.dexItemFactory.objectMethods.getClass) {
-        continue;
-      }
-      Value in = invoke.getReceiver();
-      if (in.hasLocalInfo()) {
-        continue;
-      }
-      TypeLatticeElement inType = in.getTypeLattice();
-      // Check the receiver is either class type or array type. Also make sure it is not nullable.
-      if (!(inType.isClassType() || inType.isArrayType())
-          || inType.isNullable()) {
-        continue;
-      }
-      DexType type = inType.isClassType()
-          ? inType.asClassTypeLatticeElement().getClassType()
-          : inType.asArrayTypeLatticeElement().getArrayType(appInfo.dexItemFactory);
-      DexType baseType = type.toBaseType(appInfo.dexItemFactory);
-      // Make sure base type is a class type.
-      if (!baseType.isClassType()) {
-        continue;
-      }
-      // Only consider program class, e.g., platform can introduce sub types in different versions.
-      DexClass clazz = appInfo.definitionFor(baseType);
-      if (clazz == null || !clazz.isProgramClass()) {
-        continue;
-      }
-      // Only consider effectively final class. Exception: new Base().getClass().
-      if (!baseType.hasSubtypes()
-          || !appInfo.withLiveness().isInstantiatedIndirectly(baseType)
-          || (!in.isPhi() && in.definition.isCreatingInstanceOrArray())) {
-        // Make sure the target (base) type is visible.
-        ConstraintWithTarget constraints =
-            ConstraintWithTarget.classIsVisible(code.method.method.getHolder(), baseType, appInfo);
-        if (constraints == ConstraintWithTarget.NEVER) {
-          continue;
-        }
-        TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
-        Value value = code.createValue(typeLattice, invoke.getLocalInfo());
-        ConstClass constClass = new ConstClass(value, type);
-        it.replaceCurrentInstruction(constClass);
-      }
-    }
-    assert code.isConsistentSSA();
-  }
-
   public void removeTrivialCheckCastAndInstanceOfInstructions(
       IRCode code, boolean enableWholeProgramOptimizations) {
     if (!enableWholeProgramOptimizations) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index ecd3a7a..496e113 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -9,8 +9,19 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.google.common.base.Strings;
 
 public class ReflectionOptimizer {
@@ -57,6 +68,67 @@
     }
   }
 
+  // Rewrite getClass() call to const-class if the type of the given instance is effectively final.
+  public static void rewriteGetClass(AppInfoWithLiveness appInfo, IRCode code) {
+    InstructionIterator it = code.instructionIterator();
+    while (it.hasNext()) {
+      Instruction current = it.next();
+      // Conservatively bail out if the containing block has catch handlers.
+      // TODO(b/118509730): unless join of all catch types is ClassNotFoundException ?
+      if (current.getBlock().hasCatchHandlers()) {
+        continue;
+      }
+      if (!current.isInvokeVirtual()) {
+        continue;
+      }
+      InvokeVirtual invoke = current.asInvokeVirtual();
+      DexMethod invokedMethod = invoke.getInvokedMethod();
+      // Class<?> Object#getClass() is final and cannot be overridden.
+      if (invokedMethod != appInfo.dexItemFactory.objectMethods.getClass) {
+        continue;
+      }
+      Value in = invoke.getReceiver();
+      if (in.hasLocalInfo()) {
+        continue;
+      }
+      TypeLatticeElement inType = in.getTypeLattice();
+      // Check the receiver is either class type or array type. Also make sure it is not nullable.
+      if (!(inType.isClassType() || inType.isArrayType())
+          || inType.isNullable()) {
+        continue;
+      }
+      DexType type = inType.isClassType()
+          ? inType.asClassTypeLatticeElement().getClassType()
+          : inType.asArrayTypeLatticeElement().getArrayType(appInfo.dexItemFactory);
+      DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+      // Make sure base type is a class type.
+      if (!baseType.isClassType()) {
+        continue;
+      }
+      // Only consider program class, e.g., platform can introduce sub types in different versions.
+      DexClass clazz = appInfo.definitionFor(baseType);
+      if (clazz == null || !clazz.isProgramClass()) {
+        continue;
+      }
+      // Only consider effectively final class. Exception: new Base().getClass().
+      if (!baseType.hasSubtypes()
+          || !appInfo.isInstantiatedIndirectly(baseType)
+          || (!in.isPhi() && in.definition.isCreatingInstanceOrArray())) {
+        // Make sure the target (base) type is visible.
+        ConstraintWithTarget constraints =
+            ConstraintWithTarget.classIsVisible(code.method.method.getHolder(), baseType, appInfo);
+        if (constraints == ConstraintWithTarget.NEVER) {
+          continue;
+        }
+        TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
+        Value value = code.createValue(typeLattice, invoke.getLocalInfo());
+        ConstClass constClass = new ConstClass(value, type);
+        it.replaceCurrentInstruction(constClass);
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
   public static String computeClassName(
       DexString descriptor, DexClass holder, ClassNameComputationInfo classNameComputationInfo) {
     return computeClassName(