Refactor utils to check if value/type is always null.

Bug: 72443802, 124246610
Change-Id: I3d4811a4931051c377ef974ce6971faec3f44d13
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 0e92913..c97e609 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.google.common.base.Predicates;
@@ -63,6 +64,16 @@
     return clazz == null || clazz.initializationOfParentTypesMayHaveSideEffects(appInfo, ignore);
   }
 
+  public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
+    if (isClassType()) {
+      DexClass clazz = appView.definitionFor(this);
+      return clazz != null
+          && clazz.isProgramClass()
+          && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(this);
+    }
+    return false;
+  }
+
   public boolean isSamePackage(DexType other) {
     return getPackageDescriptor().equals(other.getPackageDescriptor());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index d120911..23d3621 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ir.regalloc.LiveIntervals;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -445,6 +446,20 @@
     return !users.isEmpty() || !phiUsers.isEmpty() || numberOfDebugUsers() > 0;
   }
 
+  public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
+    if (hasLocalInfo()) {
+      // Not always null as the value can be changed via the debugger.
+      return false;
+    }
+    if (typeLattice.isDefinitelyNull()) {
+      return true;
+    }
+    if (typeLattice.isClassType()) {
+      return typeLattice.asClassTypeLatticeElement().getClassType().isAlwaysNull(appView);
+    }
+    return false;
+  }
+
   public boolean usedInMonitorOperation() {
     for (Instruction instruction : uniqueUsers()) {
       if (instruction.isMonitor()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index de060cf..7ca48ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -162,7 +162,7 @@
       for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
         RewrittenPrototypeDescription prototypeChanges =
             new RewrittenPrototypeDescription(
-                isAlwaysNull(virtualMethod.method.proto.returnType),
+                virtualMethod.method.proto.returnType.isAlwaysNull(appView),
                 getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
         if (!prototypeChanges.isEmpty()) {
           DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
@@ -283,7 +283,7 @@
       return RewrittenPrototypeDescription.none();
     }
     return new RewrittenPrototypeDescription(
-        isAlwaysNull(encodedMethod.method.proto.returnType),
+        encodedMethod.method.proto.returnType.isAlwaysNull(appView),
         getRemovedArgumentsInfo(encodedMethod, strategy));
   }
 
@@ -298,7 +298,7 @@
     int offset = encodedMethod.isStatic() ? 0 : 1;
     for (int i = 0; i < proto.parameters.size(); ++i) {
       DexType type = proto.parameters.values[i];
-      if (isAlwaysNull(type)) {
+      if (type.isAlwaysNull(appView)) {
         if (removedArgumentsInfo == null) {
           removedArgumentsInfo = new ArrayList<>();
         }
@@ -420,7 +420,7 @@
     boolean replacedByThrowNull = false;
 
     Value receiver = instruction.inValues().get(0);
-    if (isAlwaysNull(receiver)) {
+    if (receiver.isAlwaysNull(appView)) {
       // Unable to rewrite instruction if the receiver is defined from "const-number 0", since this
       // would lead to an IncompatibleClassChangeError (see MemberResolutionTest#lookupStaticField-
       // WithFieldGetFromNullReferenceDirectly).
@@ -456,7 +456,7 @@
       IRCode code,
       Set<BasicBlock> blocksToBeRemoved) {
     DexType fieldType = instruction.getField().type;
-    if (isAlwaysNull(fieldType)) {
+    if (fieldType.isAlwaysNull(appView)) {
       // Before trying to remove this instruction, we need to be sure that the field actually
       // exists. Otherwise this instruction would throw a NoSuchFieldError exception.
       DexEncodedField field = appView.definitionFor(instruction.getField());
@@ -511,7 +511,7 @@
       Set<BasicBlock> blocksToBeRemoved) {
     if (invoke.isInvokeMethodWithReceiver()) {
       Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
-      if (isAlwaysNull(receiver)) {
+      if (receiver.isAlwaysNull(appView)) {
         replaceCurrentInstructionWithThrowNull(
             invoke, blockIterator, instructionIterator, code, blocksToBeRemoved);
         ++numberOfInvokesWithNullReceiver;
@@ -529,7 +529,7 @@
     if (facts != null) {
       for (int i = 0; i < invoke.arguments().size(); i++) {
         Value argument = invoke.arguments().get(i);
-        if (isAlwaysNull(argument) && facts.get(i)) {
+        if (argument.isAlwaysNull(appView) && facts.get(i)) {
           replaceCurrentInstructionWithThrowNull(
               invoke, blockIterator, instructionIterator, code, blocksToBeRemoved);
           ++numberOfInvokesWithNullArgument;
@@ -598,28 +598,4 @@
     }
   }
 
-  private boolean isAlwaysNull(Value value) {
-    if (value.hasLocalInfo()) {
-      // Not always null as the value can be changed via the debugger.
-      return false;
-    }
-    TypeLatticeElement typeLatticeElement = value.getTypeLattice();
-    if (typeLatticeElement.isDefinitelyNull()) {
-      return true;
-    }
-    if (typeLatticeElement.isClassType()) {
-      return isAlwaysNull(typeLatticeElement.asClassTypeLatticeElement().getClassType());
-    }
-    return false;
-  }
-
-  private boolean isAlwaysNull(DexType type) {
-    if (type.isClassType()) {
-      DexClass clazz = appView.definitionFor(type);
-      return clazz != null
-          && clazz.isProgramClass()
-          && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(type);
-    }
-    return false;
-  }
 }