Merge "Minor refactoring of class initialization analysis"
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index a0fbb59..a19d070 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -4,16 +4,31 @@
 
 package com.android.tools.r8.ir.analysis;
 
+import com.android.tools.r8.graph.AppInfo.ResolutionResult;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.DominatorTree.Assumption;
 import com.android.tools.r8.ir.code.DominatorTree.Inclusive;
+import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
@@ -229,4 +244,188 @@
       markingColor = -1;
     }
   }
+
+  public static class InstructionUtils {
+
+    public static boolean forInstanceGet(
+        InstanceGet instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      return forInstanceGetOrPut(instruction, type, appView, mode, assumption);
+    }
+
+    public static boolean forInstancePut(
+        InstancePut instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      return forInstanceGetOrPut(instruction, type, appView, mode, assumption);
+    }
+
+    private static boolean forInstanceGetOrPut(
+        FieldInstruction instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      assert instruction.isInstanceGet() || instruction.isInstancePut();
+      if (assumption == AnalysisAssumption.NONE) {
+        Value object =
+            instruction.isInstanceGet()
+                ? instruction.asInstanceGet().object()
+                : instruction.asInstancePut().object();
+        if (object.getTypeLattice().isNullable()) {
+          // If the receiver is null we cannot be sure that the holder has been initialized.
+          return false;
+        }
+      }
+      return isTypeInitializedBy(type, instruction.getField().clazz, appView, mode);
+    }
+
+    public static boolean forInvokeDirect(
+        InvokeDirect instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      if (assumption == AnalysisAssumption.NONE) {
+        if (instruction.getReceiver().getTypeLattice().isNullable()) {
+          // If the receiver is null we cannot be sure that the holder has been initialized.
+          return false;
+        }
+      }
+      return isTypeInitializedBy(type, instruction.getInvokedMethod().holder, appView, mode);
+    }
+
+    public static boolean forInvokeStatic(
+        InvokeStatic instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      if (assumption == AnalysisAssumption.NONE) {
+        // Class initialization may fail with ExceptionInInitializerError.
+        return false;
+      }
+      return isTypeInitializedBy(type, instruction.getInvokedMethod().holder, appView, mode);
+    }
+
+    public static boolean forInvokeSuper(
+        InvokeSuper instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      if (assumption == AnalysisAssumption.NONE) {
+        if (instruction.getReceiver().getTypeLattice().isNullable()) {
+          // If the receiver is null we cannot be sure that the holder has been initialized.
+          return false;
+        }
+      }
+      if (mode == Query.DIRECTLY) {
+        // We cannot ensure exactly which class is being loaded because it depends on the runtime
+        // type of the receiver.
+        // TODO(christofferqa): We can do better if there is a unique target.
+        return false;
+      }
+      DexMethod method = instruction.getInvokedMethod();
+      DexClass enclosingClass = appView.appInfo().definitionFor(method.holder);
+      if (enclosingClass == null) {
+        return false;
+      }
+      DexType superType = enclosingClass.superType;
+      if (superType == null) {
+        return false;
+      }
+      ResolutionResult resolutionResult = appView.appInfo().resolveMethod(superType, method);
+      if (!resolutionResult.hasSingleTarget()) {
+        return false;
+      }
+      DexType holder = resolutionResult.asSingleTarget().method.holder;
+      return holder.isSubtypeOf(type, appView.appInfo());
+    }
+
+    public static boolean forInvokeVirtual(
+        InvokeVirtual instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      if (assumption == AnalysisAssumption.NONE) {
+        if (instruction.getReceiver().getTypeLattice().isNullable()) {
+          // If the receiver is null we cannot be sure that the holder has been initialized.
+          return false;
+        }
+      }
+      if (mode == Query.DIRECTLY) {
+        // We cannot ensure exactly which class is being loaded because it depends on the runtime
+        // type of the receiver.
+        // TODO(christofferqa): We can do better if there is a unique target.
+        return false;
+      }
+      DexMethod method = instruction.getInvokedMethod();
+      ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+      if (!resolutionResult.hasSingleTarget()) {
+        return false;
+      }
+      DexType holder = resolutionResult.asSingleTarget().method.holder;
+      return holder.isSubtypeOf(type, appView.appInfo());
+    }
+
+    public static boolean forNewInstance(
+        NewInstance instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      return isTypeInitializedBy(type, instruction.clazz, appView, mode);
+    }
+
+    public static boolean forStaticGet(
+        StaticGet instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      return forStaticGetOrPut(instruction, type, appView, mode, assumption);
+    }
+
+    public static boolean forStaticPut(
+        StaticPut instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      return forStaticGetOrPut(instruction, type, appView, mode, assumption);
+    }
+
+    private static boolean forStaticGetOrPut(
+        FieldInstruction instruction,
+        DexType type,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode,
+        AnalysisAssumption assumption) {
+      assert instruction.isStaticGet() || instruction.isStaticPut();
+      if (assumption == AnalysisAssumption.NONE) {
+        // Class initialization may fail with ExceptionInInitializerError.
+        return false;
+      }
+      return isTypeInitializedBy(type, instruction.getField().clazz, appView, mode);
+    }
+
+    private static boolean isTypeInitializedBy(
+        DexType typeToBeInitialized,
+        DexType typeKnownToBeInitialized,
+        AppView<? extends AppInfoWithSubtyping> appView,
+        Query mode) {
+      if (mode == Query.DIRECTLY) {
+        return typeKnownToBeInitialized == typeToBeInitialized;
+      } else {
+        return typeKnownToBeInitialized.isSubtypeOf(typeToBeInitialized, appView.appInfo());
+      }
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index acc122b..40756ae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -169,17 +170,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      if (object().getTypeLattice().isNullable()) {
-        // If the receiver is null we cannot be sure that the holder has been initialized.
-        return false;
-      }
-    }
-    DexType holder = getField().clazz;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forInstanceGet(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 48bc0c0..57fe121 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -152,17 +153,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      if (object().getTypeLattice().isNullable()) {
-        // If the receiver is null we cannot be sure that the holder has been initialized.
-        return false;
-      }
-    }
-    DexType holder = getField().clazz;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forInstancePut(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ca1180f..301f710 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -130,17 +131,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      if (getReceiver().getTypeLattice().isNullable()) {
-        // If the receiver is null we cannot be sure that the holder has been initialized.
-        return false;
-      }
-    }
-    DexType holder = getInvokedMethod().holder;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forInvokeDirect(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index e203cc6..b90d23e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -127,15 +127,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      // Class initialization may fail with ExceptionInInitializerError.
-      return false;
-    }
-    DexType holder = getInvokedMethod().holder;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forInvokeStatic(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index b02ca94..bc3d982 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -5,13 +5,12 @@
 
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.InvokeSuperRange;
-import com.android.tools.r8.graph.AppInfo.ResolutionResult;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -119,32 +118,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      if (getReceiver().getTypeLattice().isNullable()) {
-        // If the receiver is null we cannot be sure that the holder has been initialized.
-        return false;
-      }
-    }
-    if (mode == Query.DIRECTLY) {
-      // We cannot ensure exactly which class is being loaded because it depends on the runtime
-      // type of the receiver.
-      // TODO(christofferqa): We can do better if there is a unique target.
-      return false;
-    }
-    DexMethod method = getInvokedMethod();
-    DexClass enclosingClass = appView.appInfo().definitionFor(method.holder);
-    if (enclosingClass == null) {
-      return false;
-    }
-    DexType superType = enclosingClass.superType;
-    if (superType == null) {
-      return false;
-    }
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(superType, method);
-    if (!resolutionResult.hasSingleTarget()) {
-      return false;
-    }
-    DexType holder = resolutionResult.asSingleTarget().method.holder;
-    return holder.isSubtypeOf(clazz, appView.appInfo());
+    return ClassInitializationAnalysis.InstructionUtils.forInvokeSuper(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 4c2327f..7246ff1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -5,12 +5,12 @@
 
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.InvokeVirtualRange;
-import com.android.tools.r8.graph.AppInfo.ResolutionResult;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -109,24 +109,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      if (getReceiver().getTypeLattice().isNullable()) {
-        // If the receiver is null we cannot be sure that the holder has been initialized.
-        return false;
-      }
-    }
-    if (mode == Query.DIRECTLY) {
-      // We cannot ensure exactly which class is being loaded because it depends on the runtime
-      // type of the receiver.
-      // TODO(christofferqa): We can do better if there is a unique target.
-      return false;
-    }
-    DexMethod method = getInvokedMethod();
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
-    if (!resolutionResult.hasSingleTarget()) {
-      return false;
-    }
-    DexType holder = resolutionResult.asSingleTarget().method.holder;
-    return holder.isSubtypeOf(clazz, appView.appInfo());
+    return ClassInitializationAnalysis.InstructionUtils.forInvokeVirtual(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 93b1042..4de219e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -116,12 +117,8 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    DexType holder = this.clazz;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forNewInstance(
+        this, clazz, appView, mode, assumption);
   }
 
   public void markNoSpilling() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index db3838c..b79d076 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -156,15 +157,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      // Class initialization may fail with ExceptionInInitializerError.
-      return false;
-    }
-    DexType holder = getField().clazz;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forStaticGet(
+        this, clazz, appView, mode, assumption);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 4f63a1b..29ce40c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -139,15 +140,7 @@
       AppView<? extends AppInfoWithSubtyping> appView,
       Query mode,
       AnalysisAssumption assumption) {
-    if (assumption == AnalysisAssumption.NONE) {
-      // Class initialization may fail with ExceptionInInitializerError.
-      return false;
-    }
-    DexType holder = getField().clazz;
-    if (mode == Query.DIRECTLY) {
-      return holder == clazz;
-    } else {
-      return holder.isSubtypeOf(clazz, appView.appInfo());
-    }
+    return ClassInitializationAnalysis.InstructionUtils.forStaticPut(
+        this, clazz, appView, mode, assumption);
   }
 }