Merge commit 'ce97caf83654441074df1d25070f94ef3251971a' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 3cd04b1..c443620 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -33,7 +32,6 @@
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -167,10 +165,7 @@
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       PrefixRewritingMapper rewritePrefix =
           options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
-      AppInfo appInfo =
-          options.desugarState == DesugarState.ON
-              ? new AppInfoWithClassHierarchy(app)
-              : new AppInfo(app);
+      AppInfo appInfo = new AppInfo(app);
 
       final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5c815bc..5781482 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -405,8 +405,6 @@
     assert !internal.enableLambdaMerging;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
 
-    // TODO(b/137168535) Disable non-null tracking for now.
-    internal.enableNonNullTracking = false;
     internal.desugarState = getDesugarState();
     internal.encodeChecksums = getIncludeClassesChecksum();
     internal.dexClassChecksumFilter = getDexClassChecksumFilter();
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index bda3597..0b1fead 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -26,7 +26,6 @@
 import java.util.Collection;
 import java.util.concurrent.ExecutionException;
 
-@Keep
 public class ExtractMarker {
   public static class VdexOrigin extends Origin {
 
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index dea3bb8..4ba88f4 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.GraphLense;
@@ -122,7 +121,7 @@
           options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
 
       app = new L8TreePruner(options).prune(app, rewritePrefix);
-      AppInfo appInfo = new AppInfoWithClassHierarchy(app);
+      AppInfo appInfo = new AppInfo(app);
 
       AppView<?> appView = AppView.createForL8(appInfo, options, rewritePrefix);
       IRConverter converter = new IRConverter(appView, timing);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index ff26467..b1072df 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -174,8 +174,6 @@
     assert !internal.enableLambdaMerging;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
 
-    // TODO(b/137168535) Disable non-null tracking for now.
-    internal.enableNonNullTracking = false;
     assert internal.desugarState == DesugarState.ON;
     assert internal.enableInheritanceClassInDexDistributor;
     internal.enableInheritanceClassInDexDistributor = false;
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 9a26221..5fe37ab 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -100,7 +100,7 @@
 
     @Override
     public boolean registerInvokeVirtual(DexMethod method) {
-      ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+      ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
       DexEncodedMethod target =
           resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
       if (target != null && target.method != method) {
@@ -203,9 +203,7 @@
     private void addField(DexField field, boolean isStatic) {
       addType(field.type);
       DexEncodedField baseField =
-          isStatic
-              ? appInfo.lookupStaticTarget(field.holder, field)
-              : appInfo.lookupInstanceTarget(field.holder, field);
+          isStatic ? appInfo.lookupStaticTarget(field) : appInfo.lookupInstanceTarget(field);
       if (baseField != null && baseField.holder() != field.holder) {
         field = baseField.field;
       }
@@ -250,7 +248,7 @@
     private void registerMethod(ProgramMethod method) {
       DexEncodedMethod superTarget =
           appInfo
-              .resolveMethod(method.getHolder(), method.getReference())
+              .resolveMethodOn(method.getHolder(), method.getReference())
               .lookupInvokeSpecialTarget(context, appInfo);
       if (superTarget != null) {
         addMethod(superTarget.method);
@@ -275,7 +273,8 @@
       // If clazz overrides any methods in superType, we should keep those as well.
       clazz.forEachMethod(
           method -> {
-            ResolutionResult resolutionResult = appInfo.resolveMethod(superType, method.method);
+            ResolutionResult resolutionResult =
+                appInfo.resolveMethodOn(superType, method.method, superType != clazz.superType);
             DexEncodedMethod dexEncodedMethod = resolutionResult.getSingleTarget();
             if (dexEncodedMethod != null) {
               addMethod(dexEncodedMethod.method);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 51b0aaf..382dd08 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -50,7 +50,6 @@
 import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.jar.CfApplicationWriter;
-import com.android.tools.r8.kotlin.KotlinInfoCollector;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.Minifier;
 import com.android.tools.r8.naming.NamingLens;
@@ -318,11 +317,6 @@
           }
         }
 
-        // Compute kotlin info before setting the roots and before
-        // kotlin metadata annotation is removed.
-        KotlinInfoCollector.computeKotlinInfoForProgramClasses(
-            application, appView, executorService);
-
         // Add synthesized -assumenosideeffects from min api if relevant.
         if (options.isGeneratingDex()) {
           if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index e9997b0..6f4e958 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -167,8 +167,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forBinop();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 85c4773..1290256 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -43,8 +43,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forArrayLength();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 010bcfd..02e20ea 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -88,8 +88,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forArrayGet();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 52df928..1976942 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -78,8 +78,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forArrayPut();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 19ca32e..a46e68c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -40,7 +42,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerCheckCast(type);
   }
 
@@ -59,8 +61,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forCheckCast(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forCheckCast(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index 259bfe3..8d128be 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
@@ -92,8 +92,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forBinop();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index a203742..05d8568 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -5,6 +5,8 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -71,7 +73,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerConstClass(type);
   }
 
@@ -82,8 +84,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forConstClass(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forConstClass(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 04e569b..89fae02 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -4,8 +4,9 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
@@ -40,7 +41,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerMethodHandle(handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
   }
 
@@ -58,8 +59,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forConstMethodHandle();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index a67c3ed..8283278 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -4,8 +4,9 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -40,7 +41,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerProto(type);
   }
 
@@ -58,8 +59,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forConstMethodType();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index c3be968..889a63c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -35,8 +35,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forConstInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index da5d12a..5451986 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -133,8 +133,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forConstInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 08ee865..3b113cb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -4,8 +4,8 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -65,8 +65,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forConstInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index fbc4939..94eadd1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -5,8 +5,9 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexClassAndMethod;
+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.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -64,7 +65,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     if (nameComputationInfo.needsToRegisterReference()) {
       assert item.isDexType();
       registry.registerTypeReference(item.asDexType());
@@ -81,8 +82,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forDexItemBasedConstString(item, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forDexItemBasedConstString(item, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 5812288..7e31f5b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -5,7 +5,9 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -64,7 +66,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     switch (opcode) {
       case Opcodes.GETFIELD:
         registry.registerInstanceFieldRead(field);
@@ -123,17 +125,16 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     switch (opcode) {
       case Opcodes.GETSTATIC:
-        return inliningConstraints.forStaticGet(field, invocationContext);
+        return inliningConstraints.forStaticGet(field, context);
       case Opcodes.PUTSTATIC:
-        return inliningConstraints.forStaticPut(field, invocationContext);
+        return inliningConstraints.forStaticPut(field, context);
       case Opcodes.GETFIELD:
-        return inliningConstraints.forInstanceGet(field, invocationContext);
+        return inliningConstraints.forInstanceGet(field, context);
       case Opcodes.PUTFIELD:
-        return inliningConstraints.forInstancePut(field, invocationContext);
+        return inliningConstraints.forInstancePut(field, context);
       default:
         throw new Unreachable("Unexpected opcode " + opcode);
     }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 88d5ece..72f5be9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -293,8 +294,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index 412c97c7..bbb8996 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -60,8 +60,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forJumpInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index ebc6a98..a6d0fca 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.If.Type;
@@ -93,8 +93,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forJumpInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index c2f34f7..24c81a5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.If.Type;
@@ -94,8 +94,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forJumpInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 986d11f..4ce64a4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -51,8 +51,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index a355f58..115d27a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -49,7 +51,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType context) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerInitClass(clazz);
   }
 
@@ -66,7 +68,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType context) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forInitClass(clazz, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 5d0997f..ca6b7ed 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -49,7 +51,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerTypeReference(type);
   }
 
@@ -67,8 +69,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forInstanceOf(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forInstanceOf(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index d3cc826..05f1181 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -4,8 +4,11 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -28,7 +31,15 @@
     return printer.toString();
   }
 
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  public void registerUse(UseRegistry registry, ProgramMethod context) {
+    internalRegisterUse(registry, context);
+  }
+
+  public void registerUseForDesugaring(UseRegistry registry, ClasspathMethod context) {
+    internalRegisterUse(registry, context);
+  }
+
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     // Intentionally empty.
   }
 
@@ -153,6 +164,5 @@
   }
 
   public abstract ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context);
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 1a5dd5c..1146ff2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -83,7 +84,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     switch (opcode) {
       case Opcodes.INVOKEINTERFACE:
         registry.registerInvokeInterface(method);
@@ -94,7 +95,7 @@
       case Opcodes.INVOKESPECIAL:
         if (method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
           registry.registerInvokeDirect(method);
-        } else if (method.holder == clazz) {
+        } else if (method.holder == context.getHolderType()) {
           registry.registerInvokeDirect(method);
         } else {
           registry.registerInvokeSuper(method);
@@ -196,8 +197,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     GraphLense graphLense = inliningConstraints.getGraphLense();
     AppView<?> appView = inliningConstraints.getAppView();
     DexMethod target = method;
@@ -215,7 +215,7 @@
         if (appView.dexItemFactory().isConstructor(target)) {
           type = Type.DIRECT;
           assert noNeedToUseGraphLense(target, type, graphLense);
-        } else if (target.holder == invocationContext) {
+        } else if (target.holder == context.type) {
           // The method could have been publicized.
           type = graphLense.lookupMethod(target, null, Type.DIRECT).getType();
           assert type == Type.DIRECT || type == Type.VIRTUAL;
@@ -232,37 +232,39 @@
         }
         break;
 
-      case Opcodes.INVOKESTATIC: {
-        // Static invokes may have changed as a result of horizontal class merging.
-        GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Type.STATIC);
-        target = lookup.getMethod();
-        type = lookup.getType();
-        break;
-      }
-
-      case Opcodes.INVOKEVIRTUAL: {
-        type = Type.VIRTUAL;
-        // Instructions that target a private method in the same class translates to
-        // invoke-direct.
-        if (target.holder == invocationContext) {
-          DexClass clazz = appView.definitionFor(target.holder);
-          if (clazz != null && clazz.lookupDirectMethod(target) != null) {
-            type = Type.DIRECT;
-          }
+      case Opcodes.INVOKESTATIC:
+        {
+          // Static invokes may have changed as a result of horizontal class merging.
+          GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Type.STATIC);
+          target = lookup.getMethod();
+          type = lookup.getType();
         }
-
-        // Virtual invokes may have changed to interface invokes as a result of member rebinding.
-        GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type);
-        target = lookup.getMethod();
-        type = lookup.getType();
         break;
-      }
+
+      case Opcodes.INVOKEVIRTUAL:
+        {
+          type = Type.VIRTUAL;
+          // Instructions that target a private method in the same class translates to
+          // invoke-direct.
+          if (target.holder == context.type) {
+            DexClass clazz = appView.definitionFor(target.holder);
+            if (clazz != null && clazz.lookupDirectMethod(target) != null) {
+              type = Type.DIRECT;
+            }
+          }
+
+          // Virtual invokes may have changed to interface invokes as a result of member rebinding.
+          GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type);
+          target = lookup.getMethod();
+          type = lookup.getType();
+        }
+        break;
 
       default:
         throw new Unreachable("Unexpected opcode " + opcode);
     }
 
-    return inliningConstraints.forInvoke(target, type, invocationContext);
+    return inliningConstraints.forInvoke(target, type, context);
   }
 
   private static boolean noNeedToUseGraphLense(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 458bc23..fac04cd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -6,7 +6,9 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
@@ -82,7 +84,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerCallSite(callSite);
   }
 
@@ -110,8 +112,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forInvokeCustom();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index 7e678dc..2fc78ea 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -45,7 +45,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     throw error();
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 3901a1a..6cc49de 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -58,8 +58,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 80d6a8e..0680d73 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -87,8 +87,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forLoad();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index c2775a0..f34a7d7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -139,8 +139,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forBinop();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index 6386b3c..7fd0167 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Monitor.Type;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -52,8 +52,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forMonitor();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index d12d76b..1f787f8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -45,7 +47,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerTypeReference(type);
   }
 
@@ -64,8 +66,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forInvokeMultiNewArray(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forInvokeMultiNewArray(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index 02b2a83..cd61c29 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -81,8 +81,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forUnop();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index be087af..8bd1249 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -39,7 +41,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     registry.registerNewInstance(type);
   }
 
@@ -55,8 +57,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forNewInstance(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forNewInstance(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 1ce6048..9ef5f1b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -5,6 +5,8 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.UseRegistry;
@@ -78,7 +80,7 @@
   }
 
   @Override
-  public void registerUse(UseRegistry registry, DexType clazz) {
+  void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
     if (!type.isPrimitiveArrayType()) {
       registry.registerTypeReference(type);
     }
@@ -98,8 +100,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
-    return inliningConstraints.forNewArrayEmpty(type, invocationContext);
+      InliningConstraints inliningConstraints, DexProgramClass context) {
+    return inliningConstraints.forNewArrayEmpty(type, context);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index be9170c..4578bff 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -39,8 +39,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index dbedc8a..afbff92 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -152,8 +152,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forUnop();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 2dac0c6..93febbb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -67,8 +67,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index e1b6de1..5934617 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -75,8 +75,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forReturn();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index 0bbe1cd..c9d7f51 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -44,8 +44,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forReturn();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index 5821c3f..ef291d0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -315,8 +315,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index d560b82..d8f13d1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -86,8 +86,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forStore();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index bfbe30f..3169da0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -103,8 +103,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forJumpInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index c0c094d..9b88cb5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
 import com.android.tools.r8.ir.conversion.CfState;
@@ -46,8 +46,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints,
-      DexType invocationContext) {
+      InliningConstraints inliningConstraints, DexProgramClass context) {
     return inliningConstraints.forJumpInstruction();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index ef53aaa..0b5d100 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -3,28 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.graph.ResolutionResult.ArrayCloneMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.ClassNotFoundResult;
-import com.android.tools.r8.graph.ResolutionResult.IllegalAccessOrNoSuchMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult;
-import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 
 public class AppInfo implements DexDefinitionSupplier {
@@ -33,29 +22,44 @@
   private final DexItemFactory dexItemFactory;
 
   // TODO(b/151804585): Remove this cache.
-  private final ConcurrentHashMap<DexType, Map<DexField, DexEncodedField>> fieldDefinitionsCache =
-      new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexType, Map<DexField, DexEncodedField>> fieldDefinitionsCache;
 
   // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve the current
   // class being optimized.
-  private final ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses =
-      new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses;
 
   // Set when a new AppInfo replaces a previous one. All public methods should verify that the
   // current instance is not obsolete, to ensure that we almost use the most recent AppInfo.
-  private boolean obsolete;
+  private final BooleanBox obsolete;
 
   public AppInfo(DexApplication application) {
-    this.app = application;
-    this.dexItemFactory = app.dexItemFactory;
+    this(application, new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new BooleanBox());
   }
 
-  protected AppInfo(AppInfo previous) {
-    assert !previous.isObsolete();
-    this.app = previous.app;
-    this.dexItemFactory = app.dexItemFactory;
-    this.fieldDefinitionsCache.putAll(previous.fieldDefinitionsCache);
-    copyMetadataFromPrevious(previous);
+  // For desugaring.
+  protected AppInfo(AppInfo appInfo) {
+    this(appInfo.app, appInfo.fieldDefinitionsCache, appInfo.synthesizedClasses, appInfo.obsolete);
+  }
+
+  // For AppInfoWithLiveness.
+  protected AppInfo(AppInfoWithClassHierarchy previous) {
+    this(
+        ((AppInfo) previous).app,
+        new ConcurrentHashMap<>(((AppInfo) previous).fieldDefinitionsCache),
+        new ConcurrentHashMap<>(((AppInfo) previous).synthesizedClasses),
+        new BooleanBox());
+  }
+
+  private AppInfo(
+      DexApplication application,
+      ConcurrentHashMap<DexType, Map<DexField, DexEncodedField>> fieldDefinitionsCache,
+      ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses,
+      BooleanBox obsolete) {
+    this.app = application;
+    this.dexItemFactory = application.dexItemFactory;
+    this.fieldDefinitionsCache = fieldDefinitionsCache;
+    this.synthesizedClasses = synthesizedClasses;
+    this.obsolete = obsolete;
   }
 
   protected InternalOptions options() {
@@ -67,15 +71,15 @@
   }
 
   public boolean isObsolete() {
-    return obsolete;
+    return obsolete.get();
   }
 
   public void markObsolete() {
-    obsolete = true;
+    obsolete.set();
   }
 
   public void unsetObsolete() {
-    obsolete = false;
+    obsolete.unset();
   }
 
   public boolean checkIfObsolete() {
@@ -205,31 +209,19 @@
     fieldDefinitionsCache.remove(type);
   }
 
-  // TODO(b/147578480): Temporary API since most of the code base use a type instead
-  // of a DexProgramClass as the invocationContext.
-  DexProgramClass toProgramClass(DexType type) {
-    assert type.isClassType();
-    return DexProgramClass.asProgramClassOrNull(definitionFor(type));
-  }
-
   /**
    * Lookup static method on the method holder, or answers null.
    *
    * @param method the method to lookup
-   * @param invocationContext the class the invoke is contained in, i.e., the holder of the caller.
+   * @param context the method the invoke is contained in, i.e., the caller.
    * @return The actual target for {@code method} if on the holder, or {@code null}.
    */
-  @Deprecated // TODO(b/147578480): Remove
-  public DexEncodedMethod lookupStaticTargetOnItself(DexMethod method, DexType invocationContext) {
-    return lookupStaticTargetOnItself(method, toProgramClass(invocationContext));
-  }
-
   public final DexEncodedMethod lookupStaticTargetOnItself(
-      DexMethod method, DexProgramClass invocationContext) {
-    if (method.holder != invocationContext.type) {
+      DexMethod method, ProgramMethod context) {
+    if (method.holder != context.getHolderType()) {
       return null;
     }
-    DexEncodedMethod singleTarget = invocationContext.lookupDirectMethod(method);
+    DexEncodedMethod singleTarget = context.getHolder().lookupDirectMethod(method);
     if (singleTarget != null && singleTarget.isStatic()) {
       return singleTarget;
     }
@@ -240,334 +232,21 @@
    * Lookup direct method on the method holder, or answers null.
    *
    * @param method the method to lookup
-   * @param invocationContext the class the invoke is contained in, i.e., the holder of the caller.
+   * @param context the method the invoke is contained in, i.e., the caller.
    * @return The actual target for {@code method} if on the holder, or {@code null}.
    */
-  @Deprecated // TODO(b/147578480): Remove
-  public DexEncodedMethod lookupDirectTargetOnItself(DexMethod method, DexType invocationContext) {
-    return lookupDirectTargetOnItself(method, toProgramClass(invocationContext));
-  }
-
   public final DexEncodedMethod lookupDirectTargetOnItself(
-      DexMethod method, DexProgramClass invocationContext) {
-    if (method.holder != invocationContext.type) {
+      DexMethod method, ProgramMethod context) {
+    if (method.holder != context.getHolderType()) {
       return null;
     }
-    DexEncodedMethod singleTarget = invocationContext.lookupDirectMethod(method);
+    DexEncodedMethod singleTarget = context.getHolder().lookupDirectMethod(method);
     if (singleTarget != null && !singleTarget.isStatic()) {
       return singleTarget;
     }
     return null;
   }
 
-  /**
-   * Implements resolution of a method descriptor against a target type.
-   *
-   * <p>This method will query the definition of the holder to decide on which resolution to use. If
-   * the holder is an interface, it delegates to {@link #resolveMethodOnInterface(DexType,
-   * DexMethod)}, otherwise {@link #resolveMethodOnClass(DexType, DexMethod)} is used.
-   *
-   * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the
-   * kind of a method reference.
-   */
-  public ResolutionResult resolveMethod(DexType holder, DexMethod method) {
-    assert checkIfObsolete();
-    if (holder.isArrayType()) {
-      return resolveMethodOnArray(holder, method);
-    }
-    DexClass definition = definitionFor(holder);
-    if (definition == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    return resolveMethod(definition, method);
-  }
-
-  public ResolutionResult resolveMethod(DexClass holder, DexMethod method) {
-    return holder.isInterface()
-        ? resolveMethodOnInterface(holder, method)
-        : resolveMethodOnClass(holder, method);
-  }
-
-  /**
-   * Implements resolution of a method descriptor against a target type.
-   *
-   * <p>The boolean isInterface parameter denotes if the method reference is an interface method
-   * reference, and if so method resolution is done according to interface method resolution.
-   *
-   * @param holder Type at which to initiate the resolution.
-   * @param method Method descriptor for resolution (the field method.holder is ignored).
-   * @param isInterface Indicates if resolution is to be done according to class or interface.
-   * @return The result of resolution.
-   */
-  public ResolutionResult resolveMethod(DexType holder, DexMethod method, boolean isInterface) {
-    assert checkIfObsolete();
-    return isInterface
-        ? resolveMethodOnInterface(holder, method)
-        : resolveMethodOnClass(holder, method);
-  }
-
-  /**
-   * Implements resolution of a method descriptor against an array type.
-   *
-   * <p>See <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-10.html#jls-10.7">Section
-   * 10.7 of the Java Language Specification</a>. All invokations will have target java.lang.Object
-   * except clone which has no target.
-   */
-  private ResolutionResult resolveMethodOnArray(DexType holder, DexMethod method) {
-    assert checkIfObsolete();
-    assert holder.isArrayType();
-    if (method.name == dexItemFactory.cloneMethodName) {
-      return ArrayCloneMethodResult.INSTANCE;
-    } else {
-      return resolveMethodOnClass(dexItemFactory.objectType, method);
-    }
-  }
-
-  /**
-   * Implements resolution of a method descriptor against a class type.
-   * <p>
-   * See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">
-   * Section 5.4.3.3 of the JVM Spec</a>.
-   * <p>
-   * The resolved method is not the method that will actually be invoked. Which methods gets
-   * invoked depends on the invoke instruction used. However, it is always safe to rewrite
-   * any invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
-   * resolved method is used as basis for dispatch.
-   */
-  public ResolutionResult resolveMethodOnClass(DexType holder, DexMethod method) {
-    assert checkIfObsolete();
-    if (holder.isArrayType()) {
-      return resolveMethodOnArray(holder, method);
-    }
-    DexClass clazz = definitionFor(holder);
-    if (clazz == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    // Step 1: If holder is an interface, resolution fails with an ICCE. We return null.
-    if (clazz.isInterface()) {
-      return IncompatibleClassResult.INSTANCE;
-    }
-    return resolveMethodOnClass(clazz, method);
-  }
-
-  public ResolutionResult resolveMethodOnClass(DexClass clazz, DexMethod method) {
-    assert checkIfObsolete();
-    assert !clazz.isInterface();
-    // Step 2:
-    ResolutionResult result = resolveMethodOnClassStep2(clazz, method, clazz);
-    if (result != null) {
-      return result;
-    }
-    // Finally Step 3:
-    return resolveMethodStep3(clazz, method);
-  }
-
-  /**
-   * Implements step 2 of method resolution on classes as per <a
-   * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
-   * 5.4.3.3 of the JVM Spec</a>.
-   */
-  private ResolutionResult resolveMethodOnClassStep2(
-      DexClass clazz, DexMethod method, DexClass initialResolutionHolder) {
-    // Pt. 1: Signature polymorphic method check.
-    // See also <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9">
-    // Section 2.9 of the JVM Spec</a>.
-    DexEncodedMethod result = clazz.lookupSignaturePolymorphicMethod(method.name, dexItemFactory);
-    if (result != null) {
-      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
-    }
-    // Pt 2: Find a method that matches the descriptor.
-    result = clazz.lookupMethod(method);
-    if (result != null) {
-      // If the resolved method is private, then it can only be accessed if the symbolic reference
-      // that initiated the resolution was the type at which the method resolved on. If that is not
-      // the case, then the error is either an IllegalAccessError, or in the case where access is
-      // allowed because of nests, a NoSuchMethodError. Which error cannot be determined without
-      // knowing the calling context.
-      if (result.isPrivateMethod() && clazz != initialResolutionHolder) {
-        return new IllegalAccessOrNoSuchMethodResult(result);
-      }
-      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
-    }
-    // Pt 3: Apply step two to direct superclass of holder.
-    if (clazz.superType != null) {
-      DexClass superClass = definitionFor(clazz.superType);
-      if (superClass != null) {
-        return resolveMethodOnClassStep2(superClass, method, initialResolutionHolder);
-      }
-    }
-    return null;
-  }
-
-  /**
-   * Implements step 3 of <a
-   * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
-   * 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one
-   * implementation.
-   */
-  private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
-    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
-    resolveMethodStep3Helper(method, clazz, builder);
-    return builder.resolve(clazz);
-  }
-
-  // Non-private lookup (ie, not resolution) to find interface targets.
-  DexClassAndMethod lookupMaximallySpecificTarget(DexClass clazz, DexMethod method) {
-    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
-    resolveMethodStep3Helper(method, clazz, builder);
-    return builder.lookup();
-  }
-
-  // Non-private lookup (ie, not resolution) to find interface targets.
-  DexClassAndMethod lookupMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
-    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
-    resolveMethodStep3Helper(method, dexItemFactory.objectType, lambda.interfaces, builder);
-    return builder.lookup();
-  }
-
-  /** Helper method that builds the set of maximally specific methods. */
-  private void resolveMethodStep3Helper(
-      DexMethod method, DexClass clazz, MaximallySpecificMethodsBuilder builder) {
-    resolveMethodStep3Helper(
-        method, clazz.superType, Arrays.asList(clazz.interfaces.values), builder);
-  }
-
-  private void resolveMethodStep3Helper(
-      DexMethod method,
-      DexType superType,
-      List<DexType> interfaces,
-      MaximallySpecificMethodsBuilder builder) {
-    for (DexType iface : interfaces) {
-      DexClass definiton = definitionFor(iface);
-      if (definiton == null) {
-        // Ignore missing interface definitions.
-        continue;
-      }
-      assert definiton.isInterface();
-      DexEncodedMethod result = definiton.lookupMethod(method);
-      if (isMaximallySpecificCandidate(result)) {
-        // The candidate is added and doing so will prohibit shadowed methods from being in the set.
-        builder.addCandidate(definiton, result, this);
-      } else {
-        // Look at the super-interfaces of this class and keep searching.
-        resolveMethodStep3Helper(method, definiton, builder);
-      }
-    }
-    // Now look at indirect super interfaces.
-    if (superType != null) {
-      DexClass superClass = definitionFor(superType);
-      if (superClass != null) {
-        resolveMethodStep3Helper(method, superClass, builder);
-      }
-    }
-  }
-
-  /**
-   * A candidate for being a maximally specific method must have neither its private, nor its static
-   * flag set. A candidate may still not be maximally specific, which entails that no subinterfaces
-   * from also contribute with a candidate to the type. That is not determined by this method.
-   */
-  private boolean isMaximallySpecificCandidate(DexEncodedMethod method) {
-    return method != null && !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
-  }
-
-  /**
-   * Implements resolution of a method descriptor against an interface type.
-   * <p>
-   * See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">
-   * Section 5.4.3.4 of the JVM Spec</a>.
-   * <p>
-   * The resolved method is not the method that will actually be invoked. Which methods gets
-   * invoked depends on the invoke instruction used. However, it is always save to rewrite
-   * any invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
-   * resolved method is used as basis for dispatch.
-   */
-  public ResolutionResult resolveMethodOnInterface(DexType holder, DexMethod desc) {
-    assert checkIfObsolete();
-    if (holder.isArrayType()) {
-      return IncompatibleClassResult.INSTANCE;
-    }
-    // Step 1: Lookup interface.
-    DexClass definition = definitionFor(holder);
-    // If the definition is not an interface, resolution fails with an ICCE. We just return the
-    // empty result here.
-    if (definition == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    if (!definition.isInterface()) {
-      return IncompatibleClassResult.INSTANCE;
-    }
-    return resolveMethodOnInterface(definition, desc);
-  }
-
-  public ResolutionResult resolveMethodOnInterface(DexClass definition, DexMethod desc) {
-    assert checkIfObsolete();
-    assert definition.isInterface();
-    // Step 2: Look for exact method on interface.
-    DexEncodedMethod result = definition.lookupMethod(desc);
-    if (result != null) {
-      return new SingleResolutionResult(definition, definition, result);
-    }
-    // Step 3: Look for matching method on object class.
-    DexClass objectClass = definitionFor(dexItemFactory.objectType);
-    if (objectClass == null) {
-      return ClassNotFoundResult.INSTANCE;
-    }
-    result = objectClass.lookupMethod(desc);
-    if (result != null && result.accessFlags.isPublic() && !result.accessFlags.isAbstract()) {
-      return new SingleResolutionResult(definition, objectClass, result);
-    }
-    // Step 3: Look for maximally-specific superinterface methods or any interface definition.
-    //         This is the same for classes and interfaces.
-    return resolveMethodStep3(definition, desc);
-  }
-
-  /**
-   * Implements resolution of a field descriptor against the holder of the field. See also {@link
-   * #resolveFieldOn}.
-   */
-  public DexEncodedField resolveField(DexField field) {
-    assert checkIfObsolete();
-    return resolveFieldOn(field.holder, field);
-  }
-
-  /**
-   * Implements resolution of a field descriptor against a type.
-   * <p>
-   * See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.2">
-   * Section 5.4.3.2 of the JVM Spec</a>.
-   */
-  public DexEncodedField resolveFieldOn(DexType type, DexField desc) {
-    assert checkIfObsolete();
-    DexClass holder = definitionFor(type);
-    return holder != null ? resolveFieldOn(holder, desc) : null;
-  }
-
-  public DexEncodedField resolveFieldOn(DexClass holder, DexField desc) {
-    assert checkIfObsolete();
-    assert holder != null;
-    // Step 1: Class declares the field.
-    DexEncodedField result = holder.lookupField(desc);
-    if (result != null) {
-      return result;
-    }
-    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
-    for (DexType iface : holder.interfaces.values) {
-      result = resolveFieldOn(iface, desc);
-      if (result != null) {
-        return result;
-      }
-    }
-    // Step 3: Apply recursively to superclass.
-    if (holder.superType != null) {
-      result = resolveFieldOn(holder.superType, desc);
-      if (result != null) {
-        return result;
-      }
-    }
-    return null;
-  }
-
   public boolean hasClassHierarchy() {
     assert checkIfObsolete();
     return false;
@@ -593,107 +272,19 @@
     return app.mainDexList.contains(type);
   }
 
-  private static class MaximallySpecificMethodsBuilder {
-
-    // The set of actual maximally specific methods.
-    // This set is linked map so that in the case where a number of methods remain a deterministic
-    // choice can be made. The map is from definition classes to their maximally specific method, or
-    // in the case that a type has a candidate which is shadowed by a subinterface, the map will
-    // map the class to a null entry, thus any addition to the map must check for key containment
-    // prior to writing.
-    LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods = new LinkedHashMap<>();
-
-    void addCandidate(DexClass holder, DexEncodedMethod method, AppInfo appInfo) {
-      // If this candidate is already a candidate or it is shadowed, then no need to continue.
-      if (maximallySpecificMethods.containsKey(holder)) {
-        return;
-      }
-      maximallySpecificMethods.put(holder, method);
-      // Prune exiting candidates and prohibit future candidates in the super hierarchy.
-      assert holder.isInterface();
-      assert holder.superType == appInfo.dexItemFactory.objectType;
-      for (DexType iface : holder.interfaces.values) {
-        markShadowed(iface, appInfo);
-      }
-    }
-
-    private void markShadowed(DexType type, AppInfo appInfo) {
-      if (type == null) {
-        return;
-      }
-      DexClass clazz = appInfo.definitionFor(type);
-      if (clazz == null) {
-        return;
-      }
-      assert clazz.isInterface();
-      assert clazz.superType == appInfo.dexItemFactory.objectType;
-      // A null entry signifies that the candidate is shadowed blocking future candidates.
-      // If the candidate is already shadowed at this type there is no need to shadow further up.
-      if (maximallySpecificMethods.containsKey(clazz)
-          && maximallySpecificMethods.get(clazz) == null) {
-        return;
-      }
-      maximallySpecificMethods.put(clazz, null);
-      for (DexType iface : clazz.interfaces.values) {
-        markShadowed(iface, appInfo);
-      }
-    }
-
-    DexClassAndMethod lookup() {
-      SingleResolutionResult result = internalResolve(null).asSingleResolution();
-      return result != null
-          ? DexClassAndMethod.create(result.getResolvedHolder(), result.getResolvedMethod())
-          : null;
-    }
-
-    ResolutionResult resolve(DexClass initialResolutionHolder) {
-      assert initialResolutionHolder != null;
-      return internalResolve(initialResolutionHolder);
-    }
-
-    private ResolutionResult internalResolve(DexClass initialResolutionHolder) {
-      if (maximallySpecificMethods.isEmpty()) {
-        return NoSuchMethodResult.INSTANCE;
-      }
-      // Fast path in the common case of a single method.
-      if (maximallySpecificMethods.size() == 1) {
-        return singleResultHelper(
-            initialResolutionHolder, maximallySpecificMethods.entrySet().iterator().next());
-      }
-      Entry<DexClass, DexEncodedMethod> firstMaximallySpecificMethod = null;
-      List<Entry<DexClass, DexEncodedMethod>> nonAbstractMethods =
-          new ArrayList<>(maximallySpecificMethods.size());
-      for (Entry<DexClass, DexEncodedMethod> entry : maximallySpecificMethods.entrySet()) {
-        DexEncodedMethod method = entry.getValue();
-        if (method == null) {
-          // Ignore shadowed candidates.
-          continue;
-        }
-        if (firstMaximallySpecificMethod == null) {
-          firstMaximallySpecificMethod = entry;
-        }
-        if (method.isNonAbstractVirtualMethod()) {
-          nonAbstractMethods.add(entry);
-        }
-      }
-      // If there are no non-abstract methods, then any candidate will suffice as a target.
-      // For deterministic resolution, we return the first mapped method (of the linked map).
-      if (nonAbstractMethods.isEmpty()) {
-        return singleResultHelper(initialResolutionHolder, firstMaximallySpecificMethod);
-      }
-      // If there is exactly one non-abstract method (a default method) it is the resolution target.
-      if (nonAbstractMethods.size() == 1) {
-        return singleResultHelper(initialResolutionHolder, nonAbstractMethods.get(0));
-      }
-      return IncompatibleClassResult.create(ListUtils.map(nonAbstractMethods, Entry::getValue));
-    }
+  public final FieldResolutionResult resolveField(DexField field, ProgramMethod context) {
+    return resolveFieldOn(field.holder, field, context);
   }
 
-  private static SingleResolutionResult singleResultHelper(
-      DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
-    return new SingleResolutionResult(
-        initialResolutionResult != null ? initialResolutionResult : entry.getKey(),
-        entry.getKey(),
-        entry.getValue());
+  public FieldResolutionResult resolveFieldOn(DexType type, DexField field, ProgramMethod context) {
+    // Only allow resolution if the field is declared in the context.
+    if (type != context.getHolderType()) {
+      return FieldResolutionResult.failure();
+    }
+    DexProgramClass clazz = context.getHolder();
+    DexEncodedField definition = clazz.lookupField(field);
+    return definition != null
+        ? new SuccessfulFieldResolutionResult(clazz, clazz, definition)
+        : FieldResolutionResult.unknown();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 3d9e26c..a0fd03b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -7,15 +7,27 @@
 import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
 import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
 
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.ResolutionResult.ArrayCloneMethodResult;
+import com.android.tools.r8.graph.ResolutionResult.ClassNotFoundResult;
+import com.android.tools.r8.graph.ResolutionResult.IllegalAccessOrNoSuchMethodResult;
+import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult;
+import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
@@ -30,10 +42,21 @@
     super(application);
   }
 
-  public AppInfoWithClassHierarchy(AppInfo previous) {
+  // For desugaring.
+  private AppInfoWithClassHierarchy(AppInfo appInfo) {
+    super(appInfo);
+  }
+
+  // For AppInfoWithLiveness.
+  protected AppInfoWithClassHierarchy(AppInfoWithClassHierarchy previous) {
     super(previous);
   }
 
+  public static AppInfoWithClassHierarchy createForDesugaring(AppInfo appInfo) {
+    assert !appInfo.hasClassHierarchy();
+    return new AppInfoWithClassHierarchy(appInfo);
+  }
+
   @Override
   public boolean hasClassHierarchy() {
     assert checkIfObsolete();
@@ -329,26 +352,34 @@
    * <p>The result is the field that will be hit at runtime, if such field is known. A result of
    * null indicates that the field is either undefined or not an instance field.
    */
-  public DexEncodedField lookupInstanceTarget(DexType type, DexField field) {
+  public DexEncodedField lookupInstanceTargetOn(DexType type, DexField field) {
     assert checkIfObsolete();
     assert type.isClassType();
-    DexEncodedField result = resolveFieldOn(type, field);
+    DexEncodedField result = resolveFieldOn(type, field).getResolvedField();
     return result == null || result.accessFlags.isStatic() ? null : result;
   }
 
+  public DexEncodedField lookupInstanceTarget(DexField field) {
+    return lookupInstanceTargetOn(field.holder, field);
+  }
+
   /**
    * Lookup static field starting in type and following the interface and super chain.
    *
    * <p>The result is the field that will be hit at runtime, if such field is known. A result of
    * null indicates that the field is either undefined or not a static field.
    */
-  public DexEncodedField lookupStaticTarget(DexType type, DexField field) {
+  public DexEncodedField lookupStaticTargetOn(DexType type, DexField field) {
     assert checkIfObsolete();
     assert type.isClassType();
-    DexEncodedField result = resolveFieldOn(type, field);
+    DexEncodedField result = resolveFieldOn(type, field).getResolvedField();
     return result == null || !result.accessFlags.isStatic() ? null : result;
   }
 
+  public DexEncodedField lookupStaticTarget(DexField field) {
+    return lookupStaticTargetOn(field.holder, field);
+  }
+
   /**
    * Lookup static method following the super chain from the holder of {@code method}.
    *
@@ -358,16 +389,15 @@
    * @param method the method to lookup
    * @return The actual target for {@code method} or {@code null} if none found.
    */
-  @Deprecated // TODO(b/147578480): Remove
-  public DexEncodedMethod lookupStaticTarget(DexMethod method, DexType invocationContext) {
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupStaticTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupStaticTarget(method, toProgramClass(invocationContext));
+    return unsafeResolveMethodDueToDexFormat(method).lookupInvokeStaticTarget(context, this);
   }
 
-  public final DexEncodedMethod lookupStaticTarget(
-      DexMethod method, DexProgramClass invocationContext) {
-    assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeStaticTarget(invocationContext, this);
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupStaticTarget(DexMethod method, ProgramMethod context) {
+    return lookupStaticTarget(method, context.getHolder());
   }
 
   /**
@@ -377,19 +407,18 @@
    * non-null value if the result of resolution was an instance (i.e. non-static) method.
    *
    * @param method the method to lookup
-   * @param invocationContext the class the invoke is contained in, i.e., the holder of the caller.
+   * @param context the class the invoke is contained in, i.e., the holder of the caller.
    * @return The actual target for {@code method} or {@code null} if none found.
    */
-  @Deprecated // TODO(b/147578480): Remove
-  public DexEncodedMethod lookupSuperTarget(DexMethod method, DexType invocationContext) {
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupSuperTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupSuperTarget(method, toProgramClass(invocationContext));
+    return unsafeResolveMethodDueToDexFormat(method).lookupInvokeSuperTarget(context, this);
   }
 
-  public final DexEncodedMethod lookupSuperTarget(
-      DexMethod method, DexProgramClass invocationContext) {
-    assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeSuperTarget(invocationContext, this);
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupSuperTarget(DexMethod method, ProgramMethod context) {
+    return lookupSuperTarget(method, context.getHolder());
   }
 
   /**
@@ -400,14 +429,484 @@
    * @param method the method to lookup
    * @return The actual target for {@code method} or {@code null} if none found.
    */
-  @Deprecated // TODO(b/147578480): Remove
-  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexType invocationContext) {
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupDirectTarget(method, toProgramClass(invocationContext));
+    return unsafeResolveMethodDueToDexFormat(method).lookupInvokeDirectTarget(context, this);
   }
 
-  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass invocationContext) {
+  // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
+  public DexEncodedMethod lookupDirectTarget(DexMethod method, ProgramMethod context) {
+    return lookupDirectTarget(method, context.getHolder());
+  }
+
+  /**
+   * Implements resolution of a method descriptor.
+   *
+   * <p>This method will query the definition of the holder to decide on which resolution to use. If
+   * the holder is an interface, it delegates to {@link #resolveMethodOnInterface(DexType,
+   * DexMethod)}, otherwise {@link #resolveMethodOnClass(DexMethod, DexType)} is used.
+   *
+   * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the
+   * kind of a method reference.
+   */
+  public ResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) {
     assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeDirectTarget(invocationContext, this);
+    DexType holder = method.holder;
+    if (holder.isArrayType()) {
+      return resolveMethodOnArray(holder, method);
+    }
+    DexClass definition = definitionFor(holder);
+    if (definition == null) {
+      return ClassNotFoundResult.INSTANCE;
+    }
+    return resolveMethodOn(definition, method);
+  }
+
+  public ResolutionResult resolveMethod(DexMethod method, boolean isInterface) {
+    return isInterface
+        ? resolveMethodOnInterface(method.holder, method)
+        : resolveMethodOnClass(method, method.holder);
+  }
+
+  public ResolutionResult resolveMethodOn(DexClass holder, DexMethod method) {
+    return holder.isInterface()
+        ? resolveMethodOnInterface(holder, method)
+        : resolveMethodOnClass(method, holder);
+  }
+
+  /**
+   * Implements resolution of a method descriptor against a target type.
+   *
+   * <p>The boolean isInterface parameter denotes if the method reference is an interface method
+   * reference, and if so method resolution is done according to interface method resolution.
+   *
+   * @param holder Type at which to initiate the resolution.
+   * @param method Method descriptor for resolution (the field method.holder is ignored).
+   * @param isInterface Indicates if resolution is to be done according to class or interface.
+   * @return The result of resolution.
+   */
+  public ResolutionResult resolveMethodOn(DexType holder, DexMethod method, boolean isInterface) {
+    assert checkIfObsolete();
+    return isInterface
+        ? resolveMethodOnInterface(holder, method)
+        : resolveMethodOnClass(method, holder);
+  }
+
+  /**
+   * Implements resolution of a method descriptor against an array type.
+   *
+   * <p>See <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-10.html#jls-10.7">Section
+   * 10.7 of the Java Language Specification</a>. All invokations will have target java.lang.Object
+   * except clone which has no target.
+   */
+  private ResolutionResult resolveMethodOnArray(DexType holder, DexMethod method) {
+    assert checkIfObsolete();
+    assert holder.isArrayType();
+    if (method.name == dexItemFactory().cloneMethodName) {
+      return ArrayCloneMethodResult.INSTANCE;
+    } else {
+      return resolveMethodOnClass(method, dexItemFactory().objectType);
+    }
+  }
+
+  public ResolutionResult resolveMethodOnClass(DexMethod method) {
+    return resolveMethodOnClass(method, method.holder);
+  }
+
+  /**
+   * Implements resolution of a method descriptor against a class type.
+   *
+   * <p>See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">
+   * Section 5.4.3.3 of the JVM Spec</a>.
+   *
+   * <p>The resolved method is not the method that will actually be invoked. Which methods gets
+   * invoked depends on the invoke instruction used. However, it is always safe to rewrite any
+   * invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
+   * resolved method is used as basis for dispatch.
+   */
+  public ResolutionResult resolveMethodOnClass(DexMethod method, DexType holder) {
+    assert checkIfObsolete();
+    if (holder.isArrayType()) {
+      return resolveMethodOnArray(holder, method);
+    }
+    DexClass clazz = definitionFor(holder);
+    if (clazz == null) {
+      return ClassNotFoundResult.INSTANCE;
+    }
+    // Step 1: If holder is an interface, resolution fails with an ICCE. We return null.
+    if (clazz.isInterface()) {
+      return IncompatibleClassResult.INSTANCE;
+    }
+    return resolveMethodOnClass(method, clazz);
+  }
+
+  public ResolutionResult resolveMethodOnClass(DexMethod method, DexClass clazz) {
+    assert checkIfObsolete();
+    assert !clazz.isInterface();
+    // Step 2:
+    ResolutionResult result = resolveMethodOnClassStep2(clazz, method, clazz);
+    if (result != null) {
+      return result;
+    }
+    // Finally Step 3:
+    return resolveMethodStep3(clazz, method);
+  }
+
+  /**
+   * Implements step 2 of method resolution on classes as per <a
+   * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
+   * 5.4.3.3 of the JVM Spec</a>.
+   */
+  private ResolutionResult resolveMethodOnClassStep2(
+      DexClass clazz, DexMethod method, DexClass initialResolutionHolder) {
+    // Pt. 1: Signature polymorphic method check.
+    // See also <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9">
+    // Section 2.9 of the JVM Spec</a>.
+    DexEncodedMethod result = clazz.lookupSignaturePolymorphicMethod(method.name, dexItemFactory());
+    if (result != null) {
+      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+    }
+    // Pt 2: Find a method that matches the descriptor.
+    result = clazz.lookupMethod(method);
+    if (result != null) {
+      // If the resolved method is private, then it can only be accessed if the symbolic reference
+      // that initiated the resolution was the type at which the method resolved on. If that is not
+      // the case, then the error is either an IllegalAccessError, or in the case where access is
+      // allowed because of nests, a NoSuchMethodError. Which error cannot be determined without
+      // knowing the calling context.
+      if (result.isPrivateMethod() && clazz != initialResolutionHolder) {
+        return new IllegalAccessOrNoSuchMethodResult(result);
+      }
+      return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+    }
+    // Pt 3: Apply step two to direct superclass of holder.
+    if (clazz.superType != null) {
+      DexClass superClass = definitionFor(clazz.superType);
+      if (superClass != null) {
+        return resolveMethodOnClassStep2(superClass, method, initialResolutionHolder);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Implements step 3 of <a
+   * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
+   * 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one
+   * implementation.
+   */
+  private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
+    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+    resolveMethodStep3Helper(method, clazz, builder);
+    return builder.resolve(clazz);
+  }
+
+  // Non-private lookup (ie, not resolution) to find interface targets.
+  DexClassAndMethod lookupMaximallySpecificTarget(DexClass clazz, DexMethod method) {
+    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+    resolveMethodStep3Helper(method, clazz, builder);
+    return builder.lookup();
+  }
+
+  // Non-private lookup (ie, not resolution) to find interface targets.
+  DexClassAndMethod lookupMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
+    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+    resolveMethodStep3Helper(method, dexItemFactory().objectType, lambda.interfaces, builder);
+    return builder.lookup();
+  }
+
+  /** Helper method that builds the set of maximally specific methods. */
+  private void resolveMethodStep3Helper(
+      DexMethod method, DexClass clazz, MaximallySpecificMethodsBuilder builder) {
+    resolveMethodStep3Helper(
+        method, clazz.superType, Arrays.asList(clazz.interfaces.values), builder);
+  }
+
+  private void resolveMethodStep3Helper(
+      DexMethod method,
+      DexType superType,
+      List<DexType> interfaces,
+      MaximallySpecificMethodsBuilder builder) {
+    for (DexType iface : interfaces) {
+      DexClass definiton = definitionFor(iface);
+      if (definiton == null) {
+        // Ignore missing interface definitions.
+        continue;
+      }
+      assert definiton.isInterface();
+      DexEncodedMethod result = definiton.lookupMethod(method);
+      if (isMaximallySpecificCandidate(result)) {
+        // The candidate is added and doing so will prohibit shadowed methods from being in the set.
+        builder.addCandidate(definiton, result, this);
+      } else {
+        // Look at the super-interfaces of this class and keep searching.
+        resolveMethodStep3Helper(method, definiton, builder);
+      }
+    }
+    // Now look at indirect super interfaces.
+    if (superType != null) {
+      DexClass superClass = definitionFor(superType);
+      if (superClass != null) {
+        resolveMethodStep3Helper(method, superClass, builder);
+      }
+    }
+  }
+
+  /**
+   * A candidate for being a maximally specific method must have neither its private, nor its static
+   * flag set. A candidate may still not be maximally specific, which entails that no subinterfaces
+   * from also contribute with a candidate to the type. That is not determined by this method.
+   */
+  private boolean isMaximallySpecificCandidate(DexEncodedMethod method) {
+    return method != null && !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
+  }
+
+  public ResolutionResult resolveMethodOnInterface(DexMethod method) {
+    return resolveMethodOnInterface(method.holder, method);
+  }
+
+  /**
+   * Implements resolution of a method descriptor against an interface type.
+   *
+   * <p>See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">
+   * Section 5.4.3.4 of the JVM Spec</a>.
+   *
+   * <p>The resolved method is not the method that will actually be invoked. Which methods gets
+   * invoked depends on the invoke instruction used. However, it is always save to rewrite any
+   * invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
+   * resolved method is used as basis for dispatch.
+   */
+  public ResolutionResult resolveMethodOnInterface(DexType holder, DexMethod desc) {
+    assert checkIfObsolete();
+    if (holder.isArrayType()) {
+      return IncompatibleClassResult.INSTANCE;
+    }
+    // Step 1: Lookup interface.
+    DexClass definition = definitionFor(holder);
+    // If the definition is not an interface, resolution fails with an ICCE. We just return the
+    // empty result here.
+    if (definition == null) {
+      return ClassNotFoundResult.INSTANCE;
+    }
+    if (!definition.isInterface()) {
+      return IncompatibleClassResult.INSTANCE;
+    }
+    return resolveMethodOnInterface(definition, desc);
+  }
+
+  public ResolutionResult resolveMethodOnInterface(DexClass definition, DexMethod desc) {
+    assert checkIfObsolete();
+    assert definition.isInterface();
+    // Step 2: Look for exact method on interface.
+    DexEncodedMethod result = definition.lookupMethod(desc);
+    if (result != null) {
+      return new SingleResolutionResult(definition, definition, result);
+    }
+    // Step 3: Look for matching method on object class.
+    DexClass objectClass = definitionFor(dexItemFactory().objectType);
+    if (objectClass == null) {
+      return ClassNotFoundResult.INSTANCE;
+    }
+    result = objectClass.lookupMethod(desc);
+    if (result != null && result.accessFlags.isPublic() && !result.accessFlags.isAbstract()) {
+      return new SingleResolutionResult(definition, objectClass, result);
+    }
+    // Step 3: Look for maximally-specific superinterface methods or any interface definition.
+    //         This is the same for classes and interfaces.
+    return resolveMethodStep3(definition, desc);
+  }
+
+  /**
+   * Implements resolution of a field descriptor against the holder of the field. See also {@link
+   * #resolveFieldOn}.
+   */
+  public FieldResolutionResult resolveField(DexField field) {
+    assert checkIfObsolete();
+    return resolveFieldOn(field.holder, field);
+  }
+
+  /** Intentionally drops {@param context} since this is only needed in D8. */
+  @Override
+  public FieldResolutionResult resolveFieldOn(DexType type, DexField field, ProgramMethod context) {
+    return resolveFieldOn(type, field);
+  }
+
+  /**
+   * Implements resolution of a field descriptor against a type.
+   *
+   * <p>See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.2">
+   * Section 5.4.3.2 of the JVM Spec</a>.
+   */
+  public FieldResolutionResult resolveFieldOn(DexType type, DexField field) {
+    assert checkIfObsolete();
+    DexClass holder = definitionFor(type);
+    return holder != null ? resolveFieldOn(holder, field) : FieldResolutionResult.failure();
+  }
+
+  public FieldResolutionResult resolveFieldOn(DexClass holder, DexField field) {
+    assert checkIfObsolete();
+    assert holder != null;
+    return resolveFieldOn(holder, field, holder, SetUtils.newIdentityHashSet(8));
+  }
+
+  private FieldResolutionResult resolveFieldOn(
+      DexClass holder,
+      DexField field,
+      DexClass initialResolutionHolder,
+      Set<DexType> visitedInterfaces) {
+    assert checkIfObsolete();
+    assert holder != null;
+    // Step 1: Class declares the field.
+    DexEncodedField definition = holder.lookupField(field);
+    if (definition != null) {
+      return new SuccessfulFieldResolutionResult(initialResolutionHolder, holder, definition);
+    }
+    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
+    DexClassAndField result = resolveFieldOnDirectInterfaces(holder, field, visitedInterfaces);
+    if (result != null) {
+      return new SuccessfulFieldResolutionResult(
+          initialResolutionHolder, result.getHolder(), result.getDefinition());
+    }
+    // Step 3: Apply recursively to superclass.
+    if (holder.superType != null) {
+      DexClass superClass = definitionFor(holder.superType);
+      if (superClass != null) {
+        return resolveFieldOn(superClass, field, initialResolutionHolder, visitedInterfaces);
+      }
+    }
+    return FieldResolutionResult.failure();
+  }
+
+  private DexClassAndField resolveFieldOnDirectInterfaces(
+      DexClass clazz, DexField field, Set<DexType> visitedInterfaces) {
+    for (DexType interfaceType : clazz.interfaces.values) {
+      if (visitedInterfaces.add(interfaceType)) {
+        DexClass interfaceClass = definitionFor(interfaceType);
+        if (interfaceClass != null) {
+          DexClassAndField result =
+              resolveFieldOnInterface(interfaceClass, field, visitedInterfaces);
+          if (result != null) {
+            return result;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  private DexClassAndField resolveFieldOnInterface(
+      DexClass interfaceClass, DexField field, Set<DexType> visitedInterfaces) {
+    // Step 1: Class declares the field.
+    DexEncodedField definition = interfaceClass.lookupField(field);
+    if (definition != null) {
+      return DexClassAndField.create(interfaceClass, definition);
+    }
+    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
+    return resolveFieldOnDirectInterfaces(interfaceClass, field, visitedInterfaces);
+  }
+
+  private static class MaximallySpecificMethodsBuilder {
+
+    // The set of actual maximally specific methods.
+    // This set is linked map so that in the case where a number of methods remain a deterministic
+    // choice can be made. The map is from definition classes to their maximally specific method, or
+    // in the case that a type has a candidate which is shadowed by a subinterface, the map will
+    // map the class to a null entry, thus any addition to the map must check for key containment
+    // prior to writing.
+    LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods = new LinkedHashMap<>();
+
+    void addCandidate(DexClass holder, DexEncodedMethod method, AppInfo appInfo) {
+      // If this candidate is already a candidate or it is shadowed, then no need to continue.
+      if (maximallySpecificMethods.containsKey(holder)) {
+        return;
+      }
+      maximallySpecificMethods.put(holder, method);
+      // Prune exiting candidates and prohibit future candidates in the super hierarchy.
+      assert holder.isInterface();
+      assert holder.superType == appInfo.dexItemFactory().objectType;
+      for (DexType iface : holder.interfaces.values) {
+        markShadowed(iface, appInfo);
+      }
+    }
+
+    private void markShadowed(DexType type, AppInfo appInfo) {
+      if (type == null) {
+        return;
+      }
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null) {
+        return;
+      }
+      assert clazz.isInterface();
+      assert clazz.superType == appInfo.dexItemFactory().objectType;
+      // A null entry signifies that the candidate is shadowed blocking future candidates.
+      // If the candidate is already shadowed at this type there is no need to shadow further up.
+      if (maximallySpecificMethods.containsKey(clazz)
+          && maximallySpecificMethods.get(clazz) == null) {
+        return;
+      }
+      maximallySpecificMethods.put(clazz, null);
+      for (DexType iface : clazz.interfaces.values) {
+        markShadowed(iface, appInfo);
+      }
+    }
+
+    DexClassAndMethod lookup() {
+      SingleResolutionResult result = internalResolve(null).asSingleResolution();
+      return result != null
+          ? DexClassAndMethod.create(result.getResolvedHolder(), result.getResolvedMethod())
+          : null;
+    }
+
+    ResolutionResult resolve(DexClass initialResolutionHolder) {
+      assert initialResolutionHolder != null;
+      return internalResolve(initialResolutionHolder);
+    }
+
+    private ResolutionResult internalResolve(DexClass initialResolutionHolder) {
+      if (maximallySpecificMethods.isEmpty()) {
+        return NoSuchMethodResult.INSTANCE;
+      }
+      // Fast path in the common case of a single method.
+      if (maximallySpecificMethods.size() == 1) {
+        return singleResultHelper(
+            initialResolutionHolder, maximallySpecificMethods.entrySet().iterator().next());
+      }
+      Entry<DexClass, DexEncodedMethod> firstMaximallySpecificMethod = null;
+      List<Entry<DexClass, DexEncodedMethod>> nonAbstractMethods =
+          new ArrayList<>(maximallySpecificMethods.size());
+      for (Entry<DexClass, DexEncodedMethod> entry : maximallySpecificMethods.entrySet()) {
+        DexEncodedMethod method = entry.getValue();
+        if (method == null) {
+          // Ignore shadowed candidates.
+          continue;
+        }
+        if (firstMaximallySpecificMethod == null) {
+          firstMaximallySpecificMethod = entry;
+        }
+        if (method.isNonAbstractVirtualMethod()) {
+          nonAbstractMethods.add(entry);
+        }
+      }
+      // If there are no non-abstract methods, then any candidate will suffice as a target.
+      // For deterministic resolution, we return the first mapped method (of the linked map).
+      if (nonAbstractMethods.isEmpty()) {
+        return singleResultHelper(initialResolutionHolder, firstMaximallySpecificMethod);
+      }
+      // If there is exactly one non-abstract method (a default method) it is the resolution target.
+      if (nonAbstractMethods.size() == 1) {
+        return singleResultHelper(initialResolutionHolder, nonAbstractMethods.get(0));
+      }
+      return IncompatibleClassResult.create(ListUtils.map(nonAbstractMethods, Entry::getValue));
+    }
+
+    private static SingleResolutionResult singleResultHelper(
+        DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
+      return new SingleResolutionResult(
+          initialResolutionResult != null ? initialResolutionResult : entry.getKey(),
+          entry.getKey(),
+          entry.getValue());
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 484c3e1..a7c1351 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -38,6 +38,7 @@
   }
 
   private T appInfo;
+  private AppInfoWithClassHierarchy appInfoForDesugaring;
   private AppServices appServices;
   private final DexItemFactory dexItemFactory;
   private final WholeProgramOptimizations wholeProgramOptimizations;
@@ -144,13 +145,31 @@
   }
 
   public T appInfo() {
+    assert !appInfo.hasClassHierarchy() || enableWholeProgramOptimizations();
     return appInfo;
   }
 
+  public AppInfoWithClassHierarchy appInfoForDesugaring() {
+    if (enableWholeProgramOptimizations()) {
+      assert appInfo.hasClassHierarchy();
+      return appInfo.withClassHierarchy();
+    }
+    assert !appInfo.hasClassHierarchy();
+    if (appInfoForDesugaring == null) {
+      appInfoForDesugaring = AppInfoWithClassHierarchy.createForDesugaring(appInfo());
+    }
+    return appInfoForDesugaring;
+  }
+
+  private void unsetAppInfoForDesugaring() {
+    appInfoForDesugaring = null;
+  }
+
   public <U extends T> AppView<U> setAppInfo(U appInfo) {
     assert !appInfo.isObsolete();
     AppInfo previous = this.appInfo;
     this.appInfo = appInfo;
+    unsetAppInfoForDesugaring();
     if (appInfo != previous) {
       previous.markObsolete();
     }
@@ -434,4 +453,9 @@
         ? OptionalBool.of(appInfo().withLiveness().isSubtype(subtype, supertype))
         : OptionalBool.unknown();
   }
+
+  public boolean isCfByteCodePassThrough(DexEncodedMethod method) {
+    return options.testing.cfByteCodePassThrough != null
+        && options.testing.cfByteCodePassThrough.test(method);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index b68b839..0b9b084 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -183,7 +183,7 @@
     // Don't add parameter information if the code already has full debug information.
     // Note: This fast path can cause a method to loose its parameter info, if the debug info turned
     // out to be invalid during IR building.
-    if (appView.options().debug) {
+    if (appView.options().debug || appView.isCfByteCodePassThrough(method)) {
       return false;
     }
     assert localVariables.isEmpty();
@@ -395,23 +395,18 @@
 
   @Override
   public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
-    internalRegisterCodeReferences(method, registry);
+    for (CfInstruction instruction : instructions) {
+      instruction.registerUse(registry, method);
+    }
+    tryCatchRanges.forEach(tryCatch -> tryCatch.guards.forEach(registry::registerTypeReference));
   }
 
   @Override
   public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
-    internalRegisterCodeReferences(method, registry);
-  }
-
-  private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) {
     for (CfInstruction instruction : instructions) {
-      instruction.registerUse(registry, method.getHolderType());
+      instruction.registerUseForDesugaring(registry, method);
     }
-    for (CfTryCatch tryCatch : tryCatchRanges) {
-      for (DexType guard : tryCatch.guards) {
-        registry.registerTypeReference(guard);
-      }
-    }
+    tryCatchRanges.forEach(tryCatch -> tryCatch.guards.forEach(registry::registerTypeReference));
   }
 
   @Override
@@ -513,8 +508,7 @@
       ProgramMethod method,
       AppView<AppInfoWithLiveness> appView,
       GraphLense graphLense,
-      DexType invocationContext) {
-
+      DexProgramClass context) {
     InliningConstraints inliningConstraints = new InliningConstraints(appView, graphLense);
     if (appView.options().isInterfaceMethodDesugaringEnabled()) {
       // TODO(b/120130831): Conservatively need to say "no" at this point if there are invocations
@@ -536,9 +530,7 @@
     for (CfInstruction insn : instructions) {
       constraint =
           ConstraintWithTarget.meet(
-              constraint,
-              insn.inliningConstraint(inliningConstraints, invocationContext),
-              appView);
+              constraint, insn.inliningConstraint(inliningConstraints, context), appView);
       if (constraint == ConstraintWithTarget.NEVER) {
         return constraint;
       }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
new file mode 100644
index 0000000..7b11467
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+
+public class DexClassAndField {
+
+  private final DexClass holder;
+  private final DexEncodedField field;
+
+  DexClassAndField(DexClass holder, DexEncodedField field) {
+    assert holder != null;
+    assert field != null;
+    assert holder.type == field.holder();
+    assert holder.isProgramClass() == (this instanceof ProgramField);
+    this.holder = holder;
+    this.field = field;
+  }
+
+  public static DexClassAndField create(DexClass holder, DexEncodedField field) {
+    if (holder.isProgramClass()) {
+      return new ProgramField(holder.asProgramClass(), field);
+    } else {
+      return new DexClassAndField(holder, field);
+    }
+  }
+
+  public DexClass getHolder() {
+    return holder;
+  }
+
+  public DexType getHolderType() {
+    return holder.type;
+  }
+
+  public DexEncodedField getDefinition() {
+    return field;
+  }
+
+  public DexField getReference() {
+    return field.field;
+  }
+
+  public Origin getOrigin() {
+    return holder.origin;
+  }
+
+  public boolean isProgramField() {
+    return false;
+  }
+
+  public ProgramField asProgramField() {
+    return null;
+  }
+
+  public String toSourceString() {
+    return getReference().toSourceString();
+  }
+
+  @Override
+  public String toString() {
+    return toSourceString();
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndField");
+  }
+
+  @Override
+  public int hashCode() {
+    throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndField");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index c589524..73395b2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -231,7 +231,7 @@
   }
 
   public boolean mayTriggerClassInitializationSideEffects(
-      AppView<AppInfoWithLiveness> appView, DexType context) {
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     // Only static field matters when it comes to class initialization side effects.
     if (!isStatic()) {
       return false;
@@ -244,7 +244,7 @@
         appView,
         // Types that are a super type of the current context are guaranteed to be initialized
         // already.
-        type -> appView.isSubtype(context, type).isTrue(),
+        type -> appView.appInfo().isSubtype(context.getHolderType(), type),
         Sets.newIdentityHashSet())) {
       // Ignore class initialization side-effects for dead proto extension fields to ensure that
       // we force replace these field reads by null.
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index b81c877..c2390fb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -120,9 +120,6 @@
   public static final DexEncodedMethod SENTINEL =
       new DexEncodedMethod(
           null, null, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null);
-  public static final DexEncodedMethod ANNOTATION_REFERENCE =
-      new DexEncodedMethod(
-          null, null, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null);
   public static final Int2ReferenceMap<DebugLocalInfo> NO_PARAMETER_INFO =
       new Int2ReferenceArrayMap<>(0);
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index e27c7b4..900712b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -175,10 +175,21 @@
     return toProgramMethodOrNull(getInitializer(types));
   }
 
+  public ProgramField lookupProgramField(DexField reference) {
+    return toProgramFieldOrNull(lookupField(reference));
+  }
+
   public ProgramMethod lookupProgramMethod(DexMethod reference) {
     return toProgramMethodOrNull(getMethodCollection().getMethod(reference));
   }
 
+  private ProgramField toProgramFieldOrNull(DexEncodedField field) {
+    if (field != null) {
+      return new ProgramField(this, field);
+    }
+    return null;
+  }
+
   private ProgramMethod toProgramMethodOrNull(DexEncodedMethod method) {
     if (method != null) {
       return new ProgramMethod(this, method);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 38d3a8e..35917f5 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.graph;
 
-import java.util.Set;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -20,15 +20,15 @@
 
   int getNumberOfWriteContexts();
 
-  DexEncodedMethod getUniqueReadContext();
+  ProgramMethod getUniqueReadContext();
 
   void forEachIndirectAccess(Consumer<DexField> consumer);
 
-  void forEachIndirectAccessWithContexts(BiConsumer<DexField, Set<DexEncodedMethod>> consumer);
+  void forEachIndirectAccessWithContexts(BiConsumer<DexField, ProgramMethodSet> consumer);
 
-  void forEachReadContext(Consumer<DexEncodedMethod> consumer);
+  void forEachReadContext(Consumer<ProgramMethod> consumer);
 
-  void forEachWriteContext(Consumer<DexEncodedMethod> consumer);
+  void forEachWriteContext(Consumer<ProgramMethod> consumer);
 
   boolean hasReflectiveAccess();
 
@@ -38,17 +38,17 @@
 
   boolean isRead();
 
-  boolean isReadFromMethodHandle();
+  boolean isReadFromAnnotation();
 
-  boolean isReadOnlyIn(DexEncodedMethod method);
+  boolean isReadFromMethodHandle();
 
   boolean isWritten();
 
   boolean isWrittenFromMethodHandle();
 
-  boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
+  boolean isWrittenInMethodSatisfying(Predicate<ProgramMethod> predicate);
 
-  boolean isWrittenOnlyInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
+  boolean isWrittenOnlyInMethodSatisfying(Predicate<ProgramMethod> predicate);
 
   boolean isWrittenOutside(DexEncodedMethod method);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 5634d5b..9dd6c41 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -30,9 +30,10 @@
     return infos.get(field);
   }
 
-  public void extend(DexField field, FieldAccessInfoImpl info) {
+  public FieldAccessInfoImpl extend(DexField field, FieldAccessInfoImpl info) {
     assert !infos.containsKey(field);
     infos.put(field, info);
+    return info;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index b15153b..383c6e2 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -22,9 +23,10 @@
 
   public static final FieldAccessInfoImpl MISSING_FIELD_ACCESS_INFO = new FieldAccessInfoImpl(null);
 
-  public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 0;
-  public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 1;
-  public static int FLAG_HAS_REFLECTIVE_ACCESS = 1 << 2;
+  public static int FLAG_IS_READ_FROM_ANNOTATION = 1 << 0;
+  public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 1;
+  public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 2;
+  public static int FLAG_HAS_REFLECTIVE_ACCESS = 1 << 3;
 
   // A direct reference to the definition of the field.
   private DexField field;
@@ -34,11 +36,11 @@
 
   // Maps every direct and indirect reference in a read-context to the set of methods in which that
   // reference appears.
-  private Map<DexField, Set<DexEncodedMethod>> readsWithContexts;
+  private Map<DexField, ProgramMethodSet> readsWithContexts;
 
   // Maps every direct and indirect reference in a write-context to the set of methods in which that
   // reference appears.
-  private Map<DexField, Set<DexEncodedMethod>> writesWithContexts;
+  private Map<DexField, ProgramMethodSet> writesWithContexts;
 
   public FieldAccessInfoImpl(DexField field) {
     this.field = field;
@@ -49,10 +51,10 @@
     flattenAccessContexts(writesWithContexts);
   }
 
-  private void flattenAccessContexts(Map<DexField, Set<DexEncodedMethod>> accessesWithContexts) {
+  private void flattenAccessContexts(Map<DexField, ProgramMethodSet> accessesWithContexts) {
     if (accessesWithContexts != null) {
-      Set<DexEncodedMethod> flattenedAccessContexts =
-          accessesWithContexts.computeIfAbsent(field, ignore -> Sets.newIdentityHashSet());
+      ProgramMethodSet flattenedAccessContexts =
+          accessesWithContexts.computeIfAbsent(field, ignore -> ProgramMethodSet.create());
       accessesWithContexts.forEach(
           (access, contexts) -> {
             if (access != field) {
@@ -87,7 +89,7 @@
     return getNumberOfAccessContexts(writesWithContexts);
   }
 
-  private int getNumberOfAccessContexts(Map<DexField, Set<DexEncodedMethod>> accessesWithContexts) {
+  private int getNumberOfAccessContexts(Map<DexField, ProgramMethodSet> accessesWithContexts) {
     if (accessesWithContexts == null) {
       return 0;
     }
@@ -98,9 +100,9 @@
   }
 
   @Override
-  public DexEncodedMethod getUniqueReadContext() {
+  public ProgramMethod getUniqueReadContext() {
     if (readsWithContexts != null && readsWithContexts.size() == 1) {
-      Set<DexEncodedMethod> contexts = readsWithContexts.values().iterator().next();
+      ProgramMethodSet contexts = readsWithContexts.values().iterator().next();
       if (contexts.size() == 1) {
         return contexts.iterator().next();
       }
@@ -120,7 +122,7 @@
   }
 
   private static void forEachAccessInMap(
-      Map<DexField, Set<DexEncodedMethod>> accessesWithContexts,
+      Map<DexField, ProgramMethodSet> accessesWithContexts,
       Predicate<DexField> predicate,
       Consumer<DexField> consumer) {
     if (accessesWithContexts != null) {
@@ -134,9 +136,8 @@
   }
 
   @Override
-  public void forEachIndirectAccessWithContexts(
-      BiConsumer<DexField, Set<DexEncodedMethod>> consumer) {
-    Map<DexField, Set<DexEncodedMethod>> indirectAccessesWithContexts = new IdentityHashMap<>();
+  public void forEachIndirectAccessWithContexts(BiConsumer<DexField, ProgramMethodSet> consumer) {
+    Map<DexField, ProgramMethodSet> indirectAccessesWithContexts = new IdentityHashMap<>();
     extendAccessesWithContexts(
         indirectAccessesWithContexts, access -> access != field, readsWithContexts);
     extendAccessesWithContexts(
@@ -145,15 +146,15 @@
   }
 
   private void extendAccessesWithContexts(
-      Map<DexField, Set<DexEncodedMethod>> accessesWithContexts,
+      Map<DexField, ProgramMethodSet> accessesWithContexts,
       Predicate<DexField> predicate,
-      Map<DexField, Set<DexEncodedMethod>> extension) {
+      Map<DexField, ProgramMethodSet> extension) {
     if (extension != null) {
       extension.forEach(
           (access, contexts) -> {
             if (predicate.test(access)) {
               accessesWithContexts
-                  .computeIfAbsent(access, ignore -> Sets.newIdentityHashSet())
+                  .computeIfAbsent(access, ignore -> ProgramMethodSet.create())
                   .addAll(contexts);
             }
           });
@@ -161,24 +162,23 @@
   }
 
   @Override
-  public void forEachReadContext(Consumer<DexEncodedMethod> consumer) {
+  public void forEachReadContext(Consumer<ProgramMethod> consumer) {
     forEachAccessContext(readsWithContexts, consumer);
   }
 
   @Override
-  public void forEachWriteContext(Consumer<DexEncodedMethod> consumer) {
+  public void forEachWriteContext(Consumer<ProgramMethod> consumer) {
     forEachAccessContext(writesWithContexts, consumer);
   }
 
   private void forEachAccessContext(
-      Map<DexField, Set<DexEncodedMethod>> accessesWithContexts,
-      Consumer<DexEncodedMethod> consumer) {
+      Map<DexField, ProgramMethodSet> accessesWithContexts, Consumer<ProgramMethod> consumer) {
     // There can be indirect reads and writes of the same field reference, so we need to keep track
     // of the previously-seen indirect accesses to avoid reporting duplicates.
-    Set<DexEncodedMethod> visited = Sets.newIdentityHashSet();
+    ProgramMethodSet visited = ProgramMethodSet.create();
     if (accessesWithContexts != null) {
-      for (Set<DexEncodedMethod> encodedAccessContexts : accessesWithContexts.values()) {
-        for (DexEncodedMethod encodedAccessContext : encodedAccessContexts) {
+      for (ProgramMethodSet encodedAccessContexts : accessesWithContexts.values()) {
+        for (ProgramMethod encodedAccessContext : encodedAccessContexts) {
           if (visited.add(encodedAccessContext)) {
             consumer.accept(encodedAccessContext);
           }
@@ -199,7 +199,16 @@
   /** Returns true if this field is read by the program. */
   @Override
   public boolean isRead() {
-    return readsWithContexts != null && !readsWithContexts.isEmpty();
+    return (readsWithContexts != null && !readsWithContexts.isEmpty()) || isReadFromAnnotation();
+  }
+
+  @Override
+  public boolean isReadFromAnnotation() {
+    return (flags & FLAG_IS_READ_FROM_ANNOTATION) != 0;
+  }
+
+  public void setReadFromAnnotation() {
+    flags |= FLAG_IS_READ_FROM_ANNOTATION;
   }
 
   @Override
@@ -211,14 +220,6 @@
     flags |= FLAG_IS_READ_FROM_METHOD_HANDLE;
   }
 
-  @Override
-  public boolean isReadOnlyIn(DexEncodedMethod method) {
-    assert isRead();
-    assert method != null;
-    DexEncodedMethod uniqueReadContext = getUniqueReadContext();
-    return uniqueReadContext != null && uniqueReadContext == method;
-  }
-
   /** Returns true if this field is written by the program. */
   @Override
   public boolean isWritten() {
@@ -238,10 +239,10 @@
    * Returns true if this field is written by a method for which {@param predicate} returns true.
    */
   @Override
-  public boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate) {
+  public boolean isWrittenInMethodSatisfying(Predicate<ProgramMethod> predicate) {
     if (writesWithContexts != null) {
-      for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
-        for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
+      for (ProgramMethodSet encodedWriteContexts : writesWithContexts.values()) {
+        for (ProgramMethod encodedWriteContext : encodedWriteContexts) {
           if (predicate.test(encodedWriteContext)) {
             return true;
           }
@@ -256,10 +257,10 @@
    * true.
    */
   @Override
-  public boolean isWrittenOnlyInMethodSatisfying(Predicate<DexEncodedMethod> predicate) {
+  public boolean isWrittenOnlyInMethodSatisfying(Predicate<ProgramMethod> predicate) {
     if (writesWithContexts != null) {
-      for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
-        for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
+      for (ProgramMethodSet encodedWriteContexts : writesWithContexts.values()) {
+        for (ProgramMethod encodedWriteContext : encodedWriteContexts) {
           if (!predicate.test(encodedWriteContext)) {
             return false;
           }
@@ -275,9 +276,9 @@
   @Override
   public boolean isWrittenOutside(DexEncodedMethod method) {
     if (writesWithContexts != null) {
-      for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
-        for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
-          if (encodedWriteContext != method) {
+      for (ProgramMethodSet encodedWriteContexts : writesWithContexts.values()) {
+        for (ProgramMethod encodedWriteContext : encodedWriteContexts) {
+          if (encodedWriteContext.getDefinition() != method) {
             return true;
           }
         }
@@ -286,21 +287,21 @@
     return false;
   }
 
-  public boolean recordRead(DexField access, DexEncodedMethod context) {
+  public boolean recordRead(DexField access, ProgramMethod context) {
     if (readsWithContexts == null) {
       readsWithContexts = new IdentityHashMap<>();
     }
     return readsWithContexts
-        .computeIfAbsent(access, ignore -> Sets.newIdentityHashSet())
+        .computeIfAbsent(access, ignore -> ProgramMethodSet.create())
         .add(context);
   }
 
-  public boolean recordWrite(DexField access, DexEncodedMethod context) {
+  public boolean recordWrite(DexField access, ProgramMethod context) {
     if (writesWithContexts == null) {
       writesWithContexts = new IdentityHashMap<>();
     }
     return writesWithContexts
-        .computeIfAbsent(access, ignore -> Sets.newIdentityHashSet())
+        .computeIfAbsent(access, ignore -> ProgramMethodSet.create())
         .add(context);
   }
 
@@ -319,11 +320,11 @@
       rewritten.readsWithContexts = new IdentityHashMap<>();
       readsWithContexts.forEach(
           (access, contexts) -> {
-            Set<DexEncodedMethod> newContexts =
+            ProgramMethodSet newContexts =
                 rewritten.readsWithContexts.computeIfAbsent(
-                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
-            for (DexEncodedMethod context : contexts) {
-              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+                    lens.lookupField(access), ignore -> ProgramMethodSet.create());
+            for (ProgramMethod context : contexts) {
+              newContexts.add(lens.mapProgramMethod(context, definitions));
             }
           });
     }
@@ -331,11 +332,11 @@
       rewritten.writesWithContexts = new IdentityHashMap<>();
       writesWithContexts.forEach(
           (access, contexts) -> {
-            Set<DexEncodedMethod> newContexts =
+            ProgramMethodSet newContexts =
                 rewritten.writesWithContexts.computeIfAbsent(
-                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
-            for (DexEncodedMethod context : contexts) {
-              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+                    lens.lookupField(access), ignore -> ProgramMethodSet.create());
+            for (ProgramMethod context : contexts) {
+              newContexts.add(lens.mapProgramMethod(context, definitions));
             }
           });
     }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
new file mode 100644
index 0000000..e5d7239
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.OptionalBool;
+
+public abstract class FieldResolutionResult {
+
+  public static FailedFieldResolutionResult failure() {
+    return FailedFieldResolutionResult.INSTANCE;
+  }
+
+  public static UnknownFieldResolutionResult unknown() {
+    return UnknownFieldResolutionResult.INSTANCE;
+  }
+
+  public DexEncodedField getResolvedField() {
+    return null;
+  }
+
+  public abstract OptionalBool isAccessibleFrom(
+      ProgramMethod context, AppInfoWithClassHierarchy appInfo);
+
+  public boolean isSuccessfulResolution() {
+    return false;
+  }
+
+  public SuccessfulFieldResolutionResult asSuccessfulResolution() {
+    return null;
+  }
+
+  public boolean isFailedOrUnknownResolution() {
+    return false;
+  }
+
+  public static class SuccessfulFieldResolutionResult extends FieldResolutionResult {
+
+    private final DexClass initialResolutionHolder;
+    private final DexClass resolvedHolder;
+    private final DexEncodedField resolvedField;
+
+    SuccessfulFieldResolutionResult(
+        DexClass initialResolutionHolder, DexClass resolvedHolder, DexEncodedField resolvedField) {
+      assert resolvedHolder.type == resolvedField.holder();
+      this.initialResolutionHolder = initialResolutionHolder;
+      this.resolvedHolder = resolvedHolder;
+      this.resolvedField = resolvedField;
+    }
+
+    public DexClass getResolvedHolder() {
+      return resolvedHolder;
+    }
+
+    @Override
+    public DexEncodedField getResolvedField() {
+      return resolvedField;
+    }
+
+    public DexClassAndField getResolutionPair() {
+      return DexClassAndField.create(resolvedHolder, resolvedField);
+    }
+
+    @Override
+    public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
+      return AccessControl.isFieldAccessible(
+          resolvedField, initialResolutionHolder, context.getHolder(), appInfo);
+    }
+
+    @Override
+    public boolean isSuccessfulResolution() {
+      return true;
+    }
+
+    @Override
+    public SuccessfulFieldResolutionResult asSuccessfulResolution() {
+      return this;
+    }
+  }
+
+  public static class FailedFieldResolutionResult extends FieldResolutionResult {
+
+    private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult();
+
+    @Override
+    public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
+      return OptionalBool.FALSE;
+    }
+
+    @Override
+    public boolean isFailedOrUnknownResolution() {
+      return true;
+    }
+  }
+
+  /**
+   * Used in D8 when trying to resolve a field that is not declared on the enclosing class of the
+   * current method.
+   */
+  public static class UnknownFieldResolutionResult extends FieldResolutionResult {
+
+    private static final UnknownFieldResolutionResult INSTANCE = new UnknownFieldResolutionResult();
+
+    @Override
+    public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
+      return OptionalBool.FALSE;
+    }
+
+    @Override
+    public boolean isFailedOrUnknownResolution() {
+      return true;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index e5de7ff..2a5f317 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -145,9 +145,6 @@
   public DexEncodedMethod mapDexEncodedMethod(
       DexEncodedMethod originalEncodedMethod, DexDefinitionSupplier definitions) {
     assert originalEncodedMethod != DexEncodedMethod.SENTINEL;
-    if (originalEncodedMethod == DexEncodedMethod.ANNOTATION_REFERENCE) {
-      return DexEncodedMethod.ANNOTATION_REFERENCE;
-    }
     DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
     // Note that:
     // * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
@@ -161,6 +158,13 @@
     return newEncodedMethod;
   }
 
+  public ProgramMethod mapProgramMethod(
+      ProgramMethod oldMethod, DexDefinitionSupplier definitions) {
+    DexMethod newMethod = getRenamedMethodSignature(oldMethod.getReference());
+    DexProgramClass holder = definitions.definitionForHolder(newMethod).asProgramClass();
+    return holder.lookupProgramMethod(newMethod);
+  }
+
   public abstract DexType lookupType(DexType type);
 
   // This overload can be used when the graph lense is known to be context insensitive.
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
new file mode 100644
index 0000000..29949d8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+public class ProgramField extends DexClassAndField {
+
+  public ProgramField(DexProgramClass holder, DexEncodedField field) {
+    super(holder, field);
+  }
+
+  public boolean isStructurallyEqualTo(ProgramField other) {
+    return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
+  }
+
+  @Override
+  public boolean isProgramField() {
+    return true;
+  }
+
+  @Override
+  public ProgramField asProgramField() {
+    return this;
+  }
+
+  @Override
+  public DexProgramClass getHolder() {
+    DexClass holder = super.getHolder();
+    assert holder.isProgramClass();
+    return holder.asProgramClass();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index fd1781d..fab752df 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -62,6 +62,11 @@
   public abstract OptionalBool isAccessibleFrom(
       DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
+  public final OptionalBool isAccessibleFrom(
+      ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
+    return isAccessibleFrom(context.getHolder(), appInfo);
+  }
+
   public abstract OptionalBool isAccessibleForVirtualDispatchFrom(
       DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
@@ -228,12 +233,12 @@
      * result of resolution was a static, non-abstract method.
      *
      * @param context Class the invoke is contained in, i.e., the holder of the caller.
-     *      * @param appInfo Application info.
+     * @param appInfo Application info.
      * @return The actual target or {@code null} if none found.
      */
     @Override
-    public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo) {
+    public DexEncodedMethod lookupInvokeStaticTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       if (isAccessibleFrom(context, appInfo).isFalse()) {
         return null;
       }
@@ -265,7 +270,7 @@
     }
 
     private DexEncodedMethod internalInvokeSpecialOrSuper(
-        DexClass context,
+        DexProgramClass context,
         AppInfoWithClassHierarchy appInfo,
         BiPredicate<DexClass, DexClass> isSuperclass) {
 
@@ -669,8 +674,8 @@
     }
 
     @Override
-    public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context,
-        AppInfoWithClassHierarchy appInfo) {
+    public DexEncodedMethod lookupInvokeStaticTarget(
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 06f544d..049e211 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -4,8 +4,9 @@
 
 package com.android.tools.r8.graph.analysis;
 
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
@@ -17,10 +18,11 @@
   public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {}
 
   /** Called when a class is found to be live. */
-  public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {}
+  public void processNewlyLiveClass(
+      DexProgramClass clazz, EnqueuerWorklist worklist, DexDefinitionSupplier definitionSupplier) {}
 
   /** Called when a field is found to be live. */
-  public void processNewlyLiveField(DexEncodedField field) {}
+  public void processNewlyLiveField(ProgramField field) {}
 
   /** Called when a method is found to be live. */
   public void processNewlyLiveMethod(ProgramMethod method) {}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index 48518f5..59c37b8 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -6,7 +6,6 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -28,25 +27,26 @@
       this.mapping = mapping;
     }
 
-    public boolean isClassDefinitelyLoadedInInstanceMethodsOn(DexType subject, DexType context) {
+    public boolean isClassDefinitelyLoadedInInstanceMethod(
+        DexProgramClass subject, ProgramMethod context) {
+      assert !context.getDefinition().isStatic();
       // If `subject` is kept, then it is instantiated by reflection, which means that the analysis
       // has not seen all allocation sites. In that case, we conservatively return false.
       AppInfoWithClassHierarchy appInfo = appView.appInfo();
-      if (appInfo.hasLiveness() && appInfo.withLiveness().isPinned(subject)) {
+      if (appInfo.hasLiveness() && appInfo.withLiveness().isPinned(subject.type)) {
         return false;
       }
 
       // Check that `subject` is guaranteed to be initialized in all instance methods of `context`.
       DexType guaranteedToBeInitializedInContext =
-          mapping.getOrDefault(context, appView.dexItemFactory().objectType);
-      if (!appInfo.isSubtype(guaranteedToBeInitializedInContext, subject)) {
+          mapping.getOrDefault(context.getHolderType(), appView.dexItemFactory().objectType);
+      if (!appInfo.isSubtype(guaranteedToBeInitializedInContext, subject.type)) {
         return false;
       }
 
       // Also check that `subject` is not an interface, since interfaces are not initialized
       // transitively.
-      DexClass clazz = appView.definitionFor(subject);
-      return clazz != null && !clazz.isInterface();
+      return !subject.isInterface();
     }
   }
 
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 e825ba4..978646c 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
@@ -10,9 +10,9 @@
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-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.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -74,7 +74,6 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final IRCode code;
-  private final DexItemFactory dexItemFactory;
 
   private DominatorTree dominatorTree = null;
   private int markingColor = -1;
@@ -82,13 +81,11 @@
   private ClassInitializationAnalysis() {
     this.appView = null;
     this.code = null;
-    this.dexItemFactory = null;
   }
 
   public ClassInitializationAnalysis(AppView<AppInfoWithLiveness> appView, IRCode code) {
     this.appView = appView;
     this.code = code;
-    this.dexItemFactory = appView.dexItemFactory();
   }
 
   // Returns a trivial, conservative analysis that always returns false.
@@ -97,7 +94,7 @@
   }
 
   public boolean isClassDefinitelyLoadedBeforeInstruction(DexType type, Instruction instruction) {
-    DexType context = code.method().holder();
+    ProgramMethod context = code.context();
     BasicBlock block = instruction.getBlock();
 
     // Visit the instructions in `block` prior to `instruction`.
@@ -237,7 +234,7 @@
     public static boolean forInitClass(
         InitClass instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -251,7 +248,7 @@
     public static boolean forInstanceGet(
         InstanceGet instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       return forInstanceGetOrPut(instruction, type, appView, mode, assumption);
@@ -260,7 +257,7 @@
     public static boolean forInstancePut(
         InstancePut instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       return forInstanceGetOrPut(instruction, type, appView, mode, assumption);
@@ -269,7 +266,7 @@
     private static boolean forInstanceGetOrPut(
         FieldInstruction instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       assert instruction.isInstanceGet() || instruction.isInstancePut();
@@ -283,14 +280,15 @@
           return false;
         }
       }
-      DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+      DexEncodedField field =
+          appView.appInfo().resolveField(instruction.getField()).getResolvedField();
       return field != null && isTypeInitializedBy(instruction, type, field, appView, mode);
     }
 
     public static boolean forInvokeDirect(
         InvokeDirect instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -306,8 +304,8 @@
     public static boolean forInvokeInterface(
         InvokeInterface instruction,
         DexType type,
-        DexType context,
-        AppView<?> appView,
+        ProgramMethod context,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -342,8 +340,8 @@
     public static boolean forInvokeStatic(
         InvokeStatic instruction,
         DexType type,
-        DexType context,
-        AppView<?> appView,
+        ProgramMethod context,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -357,8 +355,8 @@
     public static boolean forInvokeSuper(
         InvokeSuper instruction,
         DexType type,
-        DexType context,
-        AppView<?> appView,
+        ProgramMethod context,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -390,7 +388,7 @@
         return false;
       }
       ResolutionResult resolutionResult =
-          appView.appInfo().resolveMethod(superType, method, instruction.itf);
+          appView.appInfo().resolveMethodOn(superType, method, instruction.itf);
       if (!resolutionResult.isSingleResolution()) {
         return false;
       }
@@ -401,8 +399,8 @@
     public static boolean forInvokeVirtual(
         InvokeVirtual instruction,
         DexType type,
-        DexType context,
-        AppView<?> appView,
+        ProgramMethod context,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -426,7 +424,7 @@
       }
       DexMethod method = instruction.getInvokedMethod();
       ResolutionResult resolutionResult =
-          appView.appInfo().resolveMethodOnClass(method.holder, method);
+          appView.appInfo().resolveMethodOnClass(method, method.holder);
       if (!resolutionResult.isSingleResolution()) {
         return false;
       }
@@ -437,7 +435,7 @@
     public static boolean forNewInstance(
         NewInstance instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       if (assumption == AnalysisAssumption.NONE) {
@@ -451,7 +449,7 @@
     public static boolean forStaticGet(
         StaticGet instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       return forStaticGetOrPut(instruction, type, appView, mode, assumption);
@@ -460,7 +458,7 @@
     public static boolean forStaticPut(
         StaticPut instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       return forStaticGetOrPut(instruction, type, appView, mode, assumption);
@@ -469,7 +467,7 @@
     private static boolean forStaticGetOrPut(
         FieldInstruction instruction,
         DexType type,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode,
         AnalysisAssumption assumption) {
       assert instruction.isStaticGet() || instruction.isStaticPut();
@@ -477,7 +475,8 @@
         // Class initialization may fail with ExceptionInInitializerError.
         return false;
       }
-      DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+      DexEncodedField field =
+          appView.appInfo().resolveField(instruction.getField()).getResolvedField();
       return field != null && isTypeInitializedBy(instruction, type, field, appView, mode);
     }
 
@@ -485,7 +484,7 @@
         Instruction instruction,
         DexType typeToBeInitialized,
         DexDefinition definition,
-        AppView<?> appView,
+        AppView<AppInfoWithLiveness> appView,
         Query mode) {
       if (mode == Query.DIRECTLY) {
         if (definition.isDexClass()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
index 5452f50..32c1bb1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -37,7 +37,7 @@
       }
       if (instr.isInvokeMethod()) {
         DexEncodedMethod target =
-            instr.asInvokeMethod().lookupSingleTarget(appView, code.method().holder());
+            instr.asInvokeMethod().lookupSingleTarget(appView, code.context());
         if (target != null && target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index 215f2a1..b73a496 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -10,6 +10,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.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DefaultInstructionVisitor;
 import com.android.tools.r8.ir.code.DominatorTree;
@@ -36,7 +37,7 @@
   public static Set<DexType> computeInitializedClassesOnNormalExit(
       AppView<AppInfoWithLiveness> appView, IRCode code) {
     DominatorTree dominatorTree = new DominatorTree(code, Assumption.MAY_HAVE_UNREACHABLE_BLOCKS);
-    Visitor visitor = new Visitor(appView, code.method().holder());
+    Visitor visitor = new Visitor(appView, code.context());
     for (BasicBlock dominator : dominatorTree.normalExitDominatorBlocks()) {
       if (dominator.hasCatchHandlers()) {
         // When determining which classes that are guaranteed to be initialized from a given
@@ -55,10 +56,10 @@
   private static class Visitor extends DefaultInstructionVisitor<Void> {
 
     private final AppView<AppInfoWithLiveness> appView;
-    private final DexType context;
+    private final ProgramMethod context;
     private final Set<DexType> initializedClassesOnNormalExit = Sets.newIdentityHashSet();
 
-    Visitor(AppView<AppInfoWithLiveness> appView, DexType context) {
+    Visitor(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
       this.appView = appView;
       this.context = context;
     }
@@ -72,7 +73,7 @@
     }
 
     private void markInitializedOnNormalExit(DexType knownToBeInitialized) {
-      if (knownToBeInitialized == context) {
+      if (knownToBeInitialized == context.getHolderType()) {
         // Do not record that the given method causes its own holder to be initialized, since this
         // is trivial.
         return;
@@ -113,7 +114,8 @@
 
     @Override
     public Void handleFieldInstruction(FieldInstruction instruction) {
-      DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+      DexEncodedField field =
+          appView.appInfo().resolveField(instruction.getField()).getResolvedField();
       if (field != null) {
         if (field.holder().isClassType()) {
           markInitializedOnNormalExit(field.holder());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index fbb6735..82e53b7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.IRCode;
@@ -27,8 +27,6 @@
 import com.android.tools.r8.utils.LongInterval;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -69,19 +67,15 @@
 
   private final AppView<?> appView;
   private final IRCode code;
-  private final DexType context;
+  private final ProgramMethod context;
 
   private final Set<Value> knownNotToDependOnEnvironment = Sets.newIdentityHashSet();
   private final Set<Value> visited = Sets.newIdentityHashSet();
 
-  // Lazily computed mapping from final field definitions of the enclosing class to the static-put
-  // instructions in the class initializer that assigns these final fields.
-  private Map<DexEncodedField, List<StaticPut>> finalFieldPuts;
-
   public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
     this.appView = appView;
     this.code = code;
-    this.context = code.method().holder();
+    this.context = code.context();
   }
 
   public boolean valueMayDependOnEnvironment(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
index 27c4e19..e4235e6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
@@ -8,7 +8,7 @@
 import static com.google.common.base.Predicates.or;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -33,9 +33,9 @@
 public class BasicBlockBehavioralSubsumption {
 
   private final AppView<?> appView;
-  private final DexType context;
+  private final ProgramMethod context;
 
-  public BasicBlockBehavioralSubsumption(AppView<?> appView, DexType context) {
+  public BasicBlockBehavioralSubsumption(AppView<?> appView, ProgramMethod context) {
     this.appView = appView;
     this.context = context;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/escape/DefaultEscapeAnalysisConfiguration.java b/src/main/java/com/android/tools/r8/ir/analysis/escape/DefaultEscapeAnalysisConfiguration.java
index 6ec09bb..b65eda2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/escape/DefaultEscapeAnalysisConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/escape/DefaultEscapeAnalysisConfiguration.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.escape;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Instruction;
 
 public class DefaultEscapeAnalysisConfiguration implements EscapeAnalysisConfiguration {
@@ -24,7 +24,7 @@
       AppView<?> appView,
       EscapeAnalysis escapeAnalysis,
       Instruction escapeRoute,
-      DexMethod context) {
+      ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
index b1cac80..b1821c3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -137,8 +138,8 @@
           }
         }
       }
-      if (!configuration.isLegitimateEscapeRoute(appView, this, user, code.method().method)
-          && isDirectlyEscaping(user, code.method().method, arguments)) {
+      if (!configuration.isLegitimateEscapeRoute(appView, this, user, code.context())
+          && isDirectlyEscaping(user, code.context(), arguments)) {
         if (stoppingCriterion.test(user)) {
           return true;
         }
@@ -173,7 +174,8 @@
     }
   }
 
-  private boolean isDirectlyEscaping(Instruction instr, DexMethod context, List<Value> arguments) {
+  private boolean isDirectlyEscaping(
+      Instruction instr, ProgramMethod context, List<Value> arguments) {
     // As return value.
     if (instr.isReturn()) {
       return true;
@@ -190,7 +192,7 @@
     if (instr.isInvokeMethod()) {
       DexMethod invokedMethod = instr.asInvokeMethod().getInvokedMethod();
       // Filter out the recursion with exactly same arguments.
-      if (invokedMethod == context) {
+      if (invokedMethod == context.getReference()) {
         return !instr.inValues().equals(arguments);
       }
       return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisConfiguration.java b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisConfiguration.java
index ea3a7e3..e3e5f4b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisConfiguration.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.escape;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Instruction;
 
 public interface EscapeAnalysisConfiguration {
@@ -14,5 +14,5 @@
       AppView<?> appView,
       EscapeAnalysis escapeAnalysis,
       Instruction escapeRoute,
-      DexMethod context);
+      ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 7c148a4..015b182 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -6,9 +6,11 @@
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -21,7 +23,7 @@
 
 public class FieldAccessAnalysis {
 
-  private final AppView<?> appView;
+  private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final FieldAssignmentTracker fieldAssignmentTracker;
   private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
 
@@ -35,7 +37,7 @@
   }
 
   public FieldAccessAnalysis(
-      AppView<?> appView,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       FieldAssignmentTracker fieldAssignmentTracker,
       FieldBitAccessAnalysis fieldBitAccessAnalysis) {
     this.appView = appView;
@@ -71,13 +73,20 @@
     for (Instruction instruction : code.instructions()) {
       if (instruction.isFieldInstruction()) {
         FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-        DexEncodedField encodedField = appView.appInfo().resolveField(fieldInstruction.getField());
-        if (encodedField != null && encodedField.isProgramField(appView)) {
-          if (fieldAssignmentTracker != null) {
-            fieldAssignmentTracker.recordFieldAccess(fieldInstruction, encodedField, code.method());
-          }
-          if (fieldBitAccessAnalysis != null) {
-            fieldBitAccessAnalysis.recordFieldAccess(fieldInstruction, encodedField, feedback);
+        FieldResolutionResult resolutionResult =
+            appView.appInfo().resolveField(fieldInstruction.getField());
+        if (resolutionResult.isSuccessfulResolution()) {
+          ProgramField field =
+              resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+          if (field != null) {
+            if (fieldAssignmentTracker != null) {
+              fieldAssignmentTracker.recordFieldAccess(
+                  fieldInstruction, field.getDefinition(), code.context());
+            }
+            if (fieldBitAccessAnalysis != null) {
+              fieldBitAccessAnalysis.recordFieldAccess(
+                  fieldInstruction, field.getDefinition(), feedback);
+            }
           }
         }
       } else if (instruction.isNewInstance()) {
@@ -85,7 +94,7 @@
         DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(newInstance.clazz));
         if (clazz != null) {
           if (fieldAssignmentTracker != null) {
-            fieldAssignmentTracker.recordAllocationSite(newInstance, clazz, code.method());
+            fieldAssignmentTracker.recordAllocationSite(newInstance, clazz, code.context());
           }
         }
       }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 5e29668..fbb6443 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -119,13 +119,13 @@
   }
 
   void recordFieldAccess(
-      FieldInstruction instruction, DexEncodedField field, DexEncodedMethod context) {
+      FieldInstruction instruction, DexEncodedField field, ProgramMethod context) {
     if (instruction.isFieldPut()) {
       recordFieldPut(field, instruction.value(), context);
     }
   }
 
-  private void recordFieldPut(DexEncodedField field, Value value, DexEncodedMethod context) {
+  private void recordFieldPut(DexEncodedField field, Value value, ProgramMethod context) {
     assert verifyValueIsConsistentWithFieldOptimizationInfo(
         value, field.getOptimizationInfo(), context);
     if (!value.isZero()) {
@@ -133,8 +133,7 @@
     }
   }
 
-  void recordAllocationSite(
-      NewInstance instruction, DexProgramClass clazz, DexEncodedMethod context) {
+  void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
     Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
         abstractInstanceFieldValues.get(clazz);
     if (abstractInstanceFieldValuesForClass == null) {
@@ -149,7 +148,7 @@
       return;
     }
 
-    DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.holder());
+    DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
     if (singleTarget == null) {
       // We just lost track.
       abstractInstanceFieldValues.remove(clazz);
@@ -174,7 +173,7 @@
               initializationInfo.asArgumentInitializationInfo();
           Value argument = invoke.arguments().get(argumentInitializationInfo.getArgumentIndex());
           AbstractValue abstractValue =
-              entry.getValue().join(argument.getAbstractValue(appView, context.holder()));
+              entry.getValue().join(argument.getAbstractValue(appView, context));
           assert !abstractValue.isBottom();
           if (!abstractValue.isUnknown()) {
             entry.setValue(abstractValue);
@@ -290,12 +289,12 @@
   }
 
   private boolean verifyValueIsConsistentWithFieldOptimizationInfo(
-      Value value, FieldOptimizationInfo optimizationInfo, DexEncodedMethod context) {
+      Value value, FieldOptimizationInfo optimizationInfo, ProgramMethod context) {
     AbstractValue abstractValue = optimizationInfo.getAbstractValue();
     if (abstractValue.isUnknown()) {
       return true;
     }
-    assert abstractValue == value.getAbstractValue(appView, context.holder());
+    assert abstractValue == value.getAbstractValue(appView, context);
     return true;
   }
 
@@ -315,7 +314,8 @@
       fieldAccessInfoCollection.flattenAccessContexts();
       fieldAccessInfoCollection.forEach(
           info -> {
-            DexEncodedField field = appView.appInfo().resolveField(info.getField());
+            DexEncodedField field =
+                appView.appInfo().resolveField(info.getField()).getResolvedField();
             if (field == null) {
               assert false;
               return;
@@ -323,7 +323,9 @@
             if (!info.hasReflectiveAccess() && !info.isWrittenFromMethodHandle()) {
               info.forEachWriteContext(
                   context ->
-                      fieldWrites.computeIfAbsent(context, ignore -> new ArrayList<>()).add(field));
+                      fieldWrites
+                          .computeIfAbsent(context.getDefinition(), ignore -> new ArrayList<>())
+                          .add(field));
               pendingFieldWrites.put(field, info.getNumberOfWriteContexts());
             }
           });
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index 1a6a707..b5bf1c4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -162,7 +162,7 @@
     }
 
     private boolean registerFieldAccess(DexField field, boolean isStatic) {
-      DexEncodedField encodedField = appView.appInfo().resolveField(field);
+      DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
       if (encodedField != null) {
         if (encodedField.isStatic() == isStatic) {
           if (fieldsOfInterest.contains(encodedField)) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 08989c0..e4bf2a9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -6,10 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.DominatorTree.Assumption;
@@ -32,10 +30,9 @@
 public abstract class FieldValueAnalysis {
 
   final AppView<AppInfoWithLiveness> appView;
-  final DexProgramClass clazz;
   final IRCode code;
+  final ProgramMethod context;
   final OptimizationFeedback feedback;
-  final DexEncodedMethod method;
 
   private DominatorTree dominatorTree;
   private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
@@ -43,18 +40,11 @@
   final Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
 
   FieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      OptimizationFeedback feedback,
-      DexProgramClass clazz,
-      DexEncodedMethod method) {
-    assert clazz != null;
-    assert clazz.type == method.holder();
+      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
     this.appView = appView;
-    this.clazz = clazz;
     this.code = code;
     this.feedback = feedback;
-    this.method = method;
+    this.context = code.context();
   }
 
   DominatorTree getOrCreateDominatorTree() {
@@ -88,7 +78,7 @@
         if (instruction.isFieldPut()) {
           FieldInstruction fieldPut = instruction.asFieldInstruction();
           DexField field = fieldPut.getField();
-          DexEncodedField encodedField = appInfo.resolveField(field);
+          DexEncodedField encodedField = appInfo.resolveField(field).getResolvedField();
           if (encodedField != null && isSubjectToOptimization(encodedField)) {
             putsPerField.computeIfAbsent(encodedField, ignore -> new LinkedList<>()).add(fieldPut);
           }
@@ -129,7 +119,6 @@
 
     // Then check if any of the instructions that precede the given instruction in the current block
     // may read the field.
-    DexType context = method.holder();
     InstructionIterator instructionIterator = block.iterator();
     while (instructionIterator.hasNext()) {
       Instruction current = instructionIterator.next();
@@ -164,7 +153,6 @@
    * and its transitive predecessors.
    */
   private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
-    DexType context = method.holder();
     Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
     Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
     while (!worklist.isEmpty()) {
@@ -249,7 +237,7 @@
   }
 
   private boolean verifyFieldSetContainsAllFieldReadsInBlock(
-      KnownFieldSet readSet, BasicBlock block, DexType context) {
+      KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
     for (Instruction instruction : block.getInstructions()) {
       AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
       assert !instructionReadSet.isTop();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 58b4194..b818cc2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -48,11 +47,9 @@
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
       OptimizationFeedback feedback,
-      DexProgramClass clazz,
-      DexEncodedMethod method,
       DexEncodedMethod parentConstructor,
       InvokeDirect parentConstructorCall) {
-    super(appView, code, feedback, clazz, method);
+    super(appView, code, feedback);
     this.factory = appView.instanceFieldInitializationInfoFactory();
     this.parentConstructor = parentConstructor;
     this.parentConstructorCall = parentConstructorCall;
@@ -67,11 +64,10 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
-      DexEncodedMethod method,
       Timing timing) {
     timing.begin("Analyze instance initializer");
     InstanceFieldInitializationInfoCollection result =
-        run(appView, code, classInitializerDefaultsResult, feedback, method);
+        run(appView, code, classInitializerDefaultsResult, feedback);
     timing.end();
     return result;
   }
@@ -80,16 +76,10 @@
       AppView<?> appView,
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
-      OptimizationFeedback feedback,
-      DexEncodedMethod method) {
+      OptimizationFeedback feedback) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
-    assert method.isInstanceInitializer();
-
-    DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass();
-    if (!appView.options().enableValuePropagationForInstanceFields) {
-      return EmptyInstanceFieldInitializationInfoCollection.getInstance();
-    }
+    assert code.context().getDefinition().isInstanceInitializer();
 
     InvokeDirect parentConstructorCall =
         IRCodeUtils.getUniqueConstructorInvoke(code.getThis(), appView.dexItemFactory());
@@ -98,7 +88,7 @@
     }
 
     DexEncodedMethod parentConstructor =
-        parentConstructorCall.lookupSingleTarget(appView, clazz.type);
+        parentConstructorCall.lookupSingleTarget(appView, code.context());
     if (parentConstructor == null) {
       return EmptyInstanceFieldInitializationInfoCollection.getInstance();
     }
@@ -108,8 +98,6 @@
             appView.withLiveness(),
             code,
             feedback,
-            clazz,
-            method,
             parentConstructor,
             parentConstructorCall);
     analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
@@ -119,7 +107,7 @@
 
   @Override
   boolean isSubjectToOptimization(DexEncodedField field) {
-    return !field.isStatic() && field.holder() == clazz.type;
+    return !field.isStatic() && field.holder() == context.getHolderType();
   }
 
   @Override
@@ -160,7 +148,7 @@
       return;
     }
 
-    AbstractValue abstractValue = value.getAbstractValue(appView, clazz.type);
+    AbstractValue abstractValue = value.getAbstractValue(appView, context);
     if (abstractValue.isSingleValue()) {
       builder.recordInitializationInfo(field, abstractValue.asSingleValue());
       return;
@@ -185,12 +173,12 @@
       return true;
     }
 
-    if (appView.appInfo().isFieldOnlyWrittenInMethod(field, method)) {
+    if (appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition())) {
       return true;
     }
 
     if (appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
-      if (parentConstructorCall.getInvokedMethod().holder != clazz.type) {
+      if (parentConstructorCall.getInvokedMethod().holder != context.getHolderType()) {
         // The field is only written in instance initializers of the enclosing class, and the
         // constructor call targets a constructor in the super class.
         return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index f488267..81df82a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -13,7 +13,6 @@
 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.DexProgramClass;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -38,12 +37,8 @@
 public class StaticFieldValueAnalysis extends FieldValueAnalysis {
 
   private StaticFieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      OptimizationFeedback feedback,
-      DexProgramClass clazz,
-      DexEncodedMethod method) {
-    super(appView, code, feedback, clazz, method);
+      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
+    super(appView, code, feedback);
   }
 
   public static void run(
@@ -51,14 +46,12 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
-      DexEncodedMethod method,
       Timing timing) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
-    assert method.isClassInitializer();
+    assert code.context().getDefinition().isClassInitializer();
     timing.begin("Analyze class initializer");
-    DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass();
-    new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
+    new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
         .computeFieldOptimizationInfo(classInitializerDefaultsResult);
     timing.end();
   }
@@ -70,7 +63,7 @@
     classInitializerDefaultsResult.forEachOptimizedField(
         (field, value) -> {
           if (putsPerField.containsKey(field)
-              || !appView.appInfo().isFieldOnlyWrittenInMethod(field, method)) {
+              || !appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition())) {
             return;
           }
 
@@ -96,15 +89,15 @@
   @Override
   boolean isSubjectToOptimization(DexEncodedField field) {
     return field.isStatic()
-        && field.holder() == clazz.type
-        && appView.appInfo().isFieldOnlyWrittenInMethod(field, method);
+        && field.holder() == context.getHolderType()
+        && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
   }
 
   @Override
   void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
     // Abstract value.
     Value root = value.getAliasedValue();
-    AbstractValue abstractValue = root.getAbstractValue(appView, clazz.type);
+    AbstractValue abstractValue = root.getAbstractValue(appView, context);
     if (abstractValue.isUnknown()) {
       feedback.recordFieldHasAbstractValue(field, appView, computeSingleFieldValue(field, root));
     } else {
@@ -149,12 +142,13 @@
    */
   private SingleFieldValue computeSingleEnumFieldValue(Value value) {
     assert !value.hasAliasedValue();
-    if (!clazz.isEnum() || !value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+    if (!context.getHolder().isEnum()
+        || !value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
       return null;
     }
 
     NewInstance newInstance = value.definition.asNewInstance();
-    if (newInstance.clazz != clazz.type) {
+    if (newInstance.clazz != context.getHolderType()) {
       return null;
     }
 
@@ -183,7 +177,8 @@
           break;
 
         case STATIC_PUT:
-          DexEncodedField field = clazz.lookupStaticField(user.asStaticPut().getField());
+          DexEncodedField field =
+              context.getHolder().lookupStaticField(user.asStaticPut().getField());
           if (field != null && field.accessFlags.isEnum()) {
             if (enumField != null) {
               return null;
@@ -219,7 +214,7 @@
       return ObjectState.empty();
     }
 
-    DexEncodedMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, clazz.type);
+    DexEncodedMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, context);
     if (singleTarget == null) {
       return ObjectState.empty();
     }
@@ -242,7 +237,7 @@
                 initializationInfo.asArgumentInitializationInfo();
             Value argument =
                 uniqueConstructorInvoke.getArgument(argumentInitializationInfo.getArgumentIndex());
-            builder.recordFieldHasValue(field, argument.getAbstractValue(appView, clazz.type));
+            builder.recordFieldHasValue(field, argument.getAbstractValue(appView, context));
           } else if (initializationInfo.isSingleValue()) {
             builder.recordFieldHasValue(field, initializationInfo.asSingleValue());
           }
@@ -251,12 +246,12 @@
   }
 
   private boolean isEnumValuesArray(Value value) {
-    assert clazz.isEnum();
+    assert context.getHolder().isEnum();
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexField valuesField =
         dexItemFactory.createField(
-            clazz.type,
-            clazz.type.toArrayType(1, dexItemFactory),
+            context.getHolderType(),
+            context.getHolderType().toArrayType(1, dexItemFactory),
             dexItemFactory.enumValuesFieldName);
 
     Value root = value.getAliasedValue();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 4879783..400b509 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -6,7 +6,6 @@
 
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -14,6 +13,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.IRCodeUtils;
@@ -178,42 +179,39 @@
             });
   }
 
-  public boolean isDeadProtoExtensionField(DexField field) {
+  public boolean isDeadProtoExtensionField(DexField fieldReference) {
     AppInfoWithLiveness appInfo = appView.appInfo();
-    DexEncodedField encodedField = appInfo.resolveField(field);
-    if (encodedField != null) {
-      return isDeadProtoExtensionField(
-          encodedField, appInfo.getFieldAccessInfoCollection(), appInfo.getPinnedItems());
+    FieldResolutionResult resolutionResult = appInfo.resolveField(fieldReference);
+    if (resolutionResult.isSuccessfulResolution()) {
+      ProgramField field =
+          resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+      return field != null
+          && isDeadProtoExtensionField(
+              field, appInfo.getFieldAccessInfoCollection(), appInfo.getPinnedItems());
     }
     return false;
   }
 
   public boolean isDeadProtoExtensionField(
-      DexEncodedField encodedField,
+      ProgramField field,
       FieldAccessInfoCollection<?> fieldAccessInfoCollection,
       Set<DexReference> pinnedItems) {
-    DexField field = encodedField.field;
-    if (pinnedItems.contains(field)) {
+    if (pinnedItems.contains(field.getReference())) {
       return false;
     }
 
-    if (field.type != references.generatedExtensionType) {
+    if (field.getReference().type != references.generatedExtensionType) {
       return false;
     }
 
-    DexClass clazz = appView.definitionFor(encodedField.holder());
-    if (clazz == null || !clazz.isProgramClass()) {
-      return false;
-    }
-
-    FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(encodedField.field);
+    FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.getReference());
     if (fieldAccessInfo == null) {
       return false;
     }
 
-    DexEncodedMethod uniqueReadContext = fieldAccessInfo.getUniqueReadContext();
+    ProgramMethod uniqueReadContext = fieldAccessInfo.getUniqueReadContext();
     return uniqueReadContext != null
-        && references.isFindLiteExtensionByNumberMethod(uniqueReadContext.method);
+        && references.isFindLiteExtensionByNumberMethod(uniqueReadContext);
   }
 
   private void forEachDeadProtoExtensionField(Consumer<DexField> consumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index c2c9a50..861c0cd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -140,6 +140,10 @@
         && method.holder != extensionRegistryLiteType;
   }
 
+  public boolean isFindLiteExtensionByNumberMethod(ProgramMethod method) {
+    return isFindLiteExtensionByNumberMethod(method.getReference());
+  }
+
   public boolean isGeneratedMessageLiteBuilder(DexProgramClass clazz) {
     return (clazz.superType == generatedMessageLiteBuilderType
             || clazz.superType == generatedMessageLiteExtendableBuilderType)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 8001121..d1bf3d1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -8,12 +8,15 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
@@ -56,7 +59,7 @@
 //  keep fields that could reach extensions to be conservative.
 public class ProtoEnqueuerExtension extends EnqueuerAnalysis {
 
-  private final AppView<?> appView;
+  private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final RawMessageInfoDecoder decoder;
   private final ProtoFieldTypeFactory factory;
   private final ProtoReferences references;
@@ -85,7 +88,7 @@
   // Mapping from extension container types to the extensions for that type.
   private final Map<DexType, Set<DexType>> extensionGraph = new IdentityHashMap<>();
 
-  public ProtoEnqueuerExtension(AppView<?> appView) {
+  public ProtoEnqueuerExtension(AppView<? extends AppInfoWithClassHierarchy> appView) {
     ProtoShrinker protoShrinker = appView.protoShrinker();
     this.appView = appView;
     this.decoder = protoShrinker.decoder;
@@ -94,7 +97,8 @@
   }
 
   @Override
-  public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
+  public void processNewlyLiveClass(
+      DexProgramClass clazz, EnqueuerWorklist worklist, DexDefinitionSupplier definitionSupplier) {
     assert appView.appInfo().hasClassHierarchy();
     AppInfoWithClassHierarchy appInfo = appView.appInfo().withClassHierarchy();
     if (appInfo.isStrictSubtypeOf(clazz.type, references.generatedMessageLiteType)) {
@@ -258,7 +262,8 @@
         Instruction definition = returnValue.definition;
         if (definition.isStaticGet()) {
           StaticGet staticGet = definition.asStaticGet();
-          DexEncodedField field = appView.appInfo().resolveField(staticGet.getField());
+          DexEncodedField field =
+              appView.appInfo().resolveField(staticGet.getField()).getResolvedField();
           if (field == null) {
             assert false;
             continue;
@@ -355,7 +360,7 @@
 
       ProgramMethod dynamicMethod = protoMessageInfo.getDynamicMethod();
       for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) {
-        DexEncodedField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo);
+        ProgramField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo);
         if (valueStorage == null) {
           continue;
         }
@@ -369,21 +374,21 @@
             // (i) optimize field reads into loading the default value of the field or (ii) remove
             // field writes to proto fields that could be read using reflection by the proto
             // library.
-            enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
+            enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
           }
           valueStorageIsLive = true;
         } else if (reachesMapOrRequiredField(protoFieldInfo)) {
           // Map/required fields cannot be removed. Therefore, we mark such fields as both read and
           // written such that we cannot optimize any field reads or writes.
-          enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
+          enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
           worklist.enqueueMarkReachableFieldAction(
-              dynamicMethod.getHolder(), valueStorage, KeepReason.reflectiveUseIn(dynamicMethod));
+              valueStorage, KeepReason.reflectiveUseIn(dynamicMethod));
           valueStorageIsLive = true;
         } else {
           valueStorageIsLive = false;
         }
 
-        DexEncodedField newlyLiveField = null;
+        ProgramField newlyLiveField = null;
         if (valueStorageIsLive) {
           // For one-of fields, mark the corresponding one-of-case field as live, and for proto2
           // singular fields, mark the corresponding hazzer-bit field as live.
@@ -391,27 +396,28 @@
             newlyLiveField = protoFieldInfo.getOneOfCaseField(appView, protoMessageInfo);
           } else if (protoFieldInfo.hasHazzerBitField(protoMessageInfo)) {
             newlyLiveField = protoFieldInfo.getHazzerBitField(appView, protoMessageInfo);
-            enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
+            enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
           }
         } else {
           // For one-of fields, mark the one-of field as live if the one-of-case field is live, and
           // for proto2 singular fields, mark the field as live if the corresponding hazzer-bit
           // field is live.
           if (protoFieldInfo.getType().isOneOf()) {
-            DexEncodedField oneOfCaseField =
+            ProgramField oneOfCaseField =
                 protoFieldInfo.getOneOfCaseField(appView, protoMessageInfo);
             if (oneOfCaseField != null && enqueuer.isFieldLive(oneOfCaseField)) {
               newlyLiveField = valueStorage;
             }
           } else if (protoFieldInfo.hasHazzerBitField(protoMessageInfo)) {
-            DexEncodedField hazzerBitField =
+            ProgramField hazzerBitField =
                 protoFieldInfo.getHazzerBitField(appView, protoMessageInfo);
             if (hazzerBitField == null || !enqueuer.isFieldLive(hazzerBitField)) {
               continue;
             }
 
             if (appView.options().enableFieldBitAccessAnalysis && appView.isAllCodeProcessed()) {
-              FieldOptimizationInfo optimizationInfo = hazzerBitField.getOptimizationInfo();
+              FieldOptimizationInfo optimizationInfo =
+                  hazzerBitField.getDefinition().getOptimizationInfo();
               int hazzerBitIndex = protoFieldInfo.getHazzerBitFieldIndex(protoMessageInfo);
               if (!BitUtils.isBitSet(optimizationInfo.getReadBits(), hazzerBitIndex)) {
                 continue;
@@ -428,20 +434,19 @@
           ProgramMethod defaultInitializer =
               dynamicMethod.getHolder().getProgramDefaultInitializer();
           assert defaultInitializer != null;
-          Predicate<DexEncodedMethod> neitherDefaultConstructorNorDynamicMethod =
+          Predicate<ProgramMethod> neitherDefaultConstructorNorDynamicMethod =
               writer ->
-                  writer != defaultInitializer.getDefinition()
-                      && writer != dynamicMethod.getDefinition();
+                  !writer.isStructurallyEqualTo(defaultInitializer)
+                      && !writer.isStructurallyEqualTo(dynamicMethod);
           if (enqueuer.isFieldWrittenInMethodSatisfying(
               newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
-            enqueuer.registerReflectiveFieldRead(newlyLiveField.field, dynamicMethod);
+            enqueuer.registerReflectiveFieldRead(newlyLiveField.getReference(), dynamicMethod);
           }
 
           // Unconditionally register the hazzer and one-of proto fields as written from
           // dynamicMethod().
-          if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.field, dynamicMethod)) {
+          if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.getReference(), dynamicMethod)) {
             worklist.enqueueMarkReachableFieldAction(
-                dynamicMethod.getHolder(),
                 newlyLiveField,
                 KeepReason.reflectiveUseIn(dynamicMethod));
           }
@@ -474,7 +479,7 @@
         // NOTE: If `valueStorage` is not a live field, then code for it will not be emitted in the
         // schema, and therefore we do need to trace the const-class instructions that will be
         // emitted for it.
-        DexEncodedField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo);
+        ProgramField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo);
         if (valueStorage != null && enqueuer.isFieldLive(valueStorage)) {
           for (ProtoObject object : objects) {
             if (object.isProtoObjectFromStaticGet()) {
@@ -511,27 +516,32 @@
       return;
     }
 
-    DexField oneOfCaseField = oneOfCaseObject.asLiveProtoFieldObject().getField();
-    DexEncodedField encodedOneOfCaseField = appView.appInfo().resolveField(oneOfCaseField);
-    if (encodedOneOfCaseField == null) {
+    DexField oneOfCaseFieldReference = oneOfCaseObject.asLiveProtoFieldObject().getField();
+    FieldResolutionResult oneOfCaseFieldResolutionResult =
+        appView.appInfo().resolveField(oneOfCaseFieldReference);
+    if (oneOfCaseFieldResolutionResult.isFailedOrUnknownResolution()) {
       assert false;
       return;
     }
 
-    DexProgramClass clazz =
-        asProgramClassOrNull(appView.definitionFor(encodedOneOfCaseField.holder()));
-    if (clazz == null) {
+    ProgramField oneOfCaseField =
+        oneOfCaseFieldResolutionResult
+            .asSuccessfulResolution()
+            .getResolutionPair()
+            .asProgramField();
+    if (oneOfCaseField == null) {
       assert false;
       return;
     }
 
-    ProgramMethod dynamicMethod = clazz.lookupProgramMethod(references.dynamicMethod);
+    ProgramMethod dynamicMethod =
+        oneOfCaseField.getHolder().lookupProgramMethod(references.dynamicMethod);
     if (dynamicMethod == null) {
       assert false;
       return;
     }
 
-    if (!enqueuer.isFieldLive(encodedOneOfCaseField)) {
+    if (!enqueuer.isFieldLive(oneOfCaseField)) {
       return;
     }
 
@@ -541,21 +551,24 @@
       return;
     }
 
-    DexField oneOfField = oneOfObject.asLiveProtoFieldObject().getField();
-    DexEncodedField encodedOneOfField = appView.appInfo().resolveField(oneOfField);
-    if (encodedOneOfField == null) {
+    DexField oneOfFieldReference = oneOfObject.asLiveProtoFieldObject().getField();
+    FieldResolutionResult oneOfFieldResolutionResult =
+        appView.appInfo().resolveField(oneOfFieldReference);
+    if (oneOfFieldResolutionResult.isFailedOrUnknownResolution()) {
       assert false;
       return;
     }
 
-    if (encodedOneOfField.holder() != encodedOneOfCaseField.holder()) {
+    ProgramField oneOfField =
+        oneOfFieldResolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    if (oneOfField == null || oneOfField.getHolder() != oneOfCaseField.getHolder()) {
       assert false;
       return;
     }
 
-    if (enqueuer.registerReflectiveFieldWrite(encodedOneOfField.field, dynamicMethod)) {
+    if (enqueuer.registerReflectiveFieldWrite(oneOfField.getReference(), dynamicMethod)) {
       worklist.enqueueMarkReachableFieldAction(
-          clazz.asProgramClass(), encodedOneOfField, KeepReason.reflectiveUseIn(dynamicMethod));
+          oneOfField, KeepReason.reflectiveUseIn(dynamicMethod));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
index 944d069..6704f91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
@@ -6,9 +6,11 @@
 
 import static com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo.BITS_PER_HAS_BITS_WORD;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import java.util.List;
 import java.util.OptionalInt;
 
@@ -120,7 +122,8 @@
     return protoMessageInfo.isProto2() && type.isSingular();
   }
 
-  public DexEncodedField getHazzerBitField(AppView<?> appView, ProtoMessageInfo protoMessageInfo) {
+  public ProgramField getHazzerBitField(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
     assert hasHazzerBitField(protoMessageInfo);
 
     int hasBitsIndex = getAuxData() / BITS_PER_HAS_BITS_WORD;
@@ -128,7 +131,12 @@
 
     ProtoObject object = protoMessageInfo.getHasBitsObjects().get(hasBitsIndex);
     assert object.isLiveProtoFieldObject();
-    return appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    FieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    if (resolutionResult.isSuccessfulResolution()) {
+      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    }
+    return null;
   }
 
   public int getHazzerBitFieldIndex(ProtoMessageInfo protoMessageInfo) {
@@ -162,12 +170,17 @@
    *   }
    * </pre>
    */
-  public DexEncodedField getOneOfCaseField(AppView<?> appView, ProtoMessageInfo protoMessageInfo) {
+  public ProgramField getOneOfCaseField(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
     assert type.isOneOf();
-
     ProtoObject object = protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfCaseObject();
     assert object.isLiveProtoFieldObject();
-    return appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    FieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    if (resolutionResult.isSuccessfulResolution()) {
+      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    }
+    return null;
   }
 
   /**
@@ -176,13 +189,19 @@
    * <p>Java field into which the value is stored; constituents of a oneof all share the same
    * storage.
    */
-  public DexEncodedField getValueStorage(AppView<?> appView, ProtoMessageInfo protoMessageInfo) {
+  public ProgramField getValueStorage(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
     ProtoObject object =
         type.isOneOf()
             ? protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfObject()
             : objects.get(0);
     assert object.isLiveProtoFieldObject();
-    return appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    FieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
+    if (resolutionResult.isSuccessfulResolution()) {
+      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    }
+    return null;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index b3aaf9c..f92edd0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.ValueMayDependOnEnvironmentAnalysis;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.IRCode;
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.code.NewArrayFilledData;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.OptionalBool;
 
 public class ClassInitializerSideEffectAnalysis {
@@ -38,8 +39,8 @@
    * non-static-put instructions may have side effects.
    */
   public static ClassInitializerSideEffect classInitializerCanBePostponed(
-      AppView<?> appView, IRCode code) {
-    DexType context = code.method().holder();
+      AppView<AppInfoWithLiveness> appView, IRCode code) {
+    ProgramMethod context = code.context();
     OptionalBool controlFlowMayDependOnEnvironment = OptionalBool.unknown();
     boolean mayHaveSideEffects = false;
 
@@ -111,9 +112,10 @@
 
       if (instruction.isStaticPut()) {
         StaticPut staticPut = instruction.asStaticPut();
-        DexEncodedField field = appView.appInfo().resolveField(staticPut.getField());
+        DexEncodedField field =
+            appView.appInfo().resolveField(staticPut.getField()).getResolvedField();
         if (field == null
-            || field.holder() != context
+            || field.holder() != context.getHolderType()
             || environmentAnalysis.valueMayDependOnEnvironment(staticPut.value())
             || instruction.instructionInstanceCanThrow(appView, context).isThrowing()) {
           return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index 76213c6..bcb5c0b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -75,7 +75,7 @@
     Value returnedValue =
         code.createValue(classClassType(appView, definitelyNotNull()), debugLocalInfo);
     ConstClass instruction = new ConstClass(returnedValue, type);
-    assert !instruction.instructionMayHaveSideEffects(appView, code.method().holder());
+    assert !instruction.instructionMayHaveSideEffects(appView, code.context());
     return instruction;
   }
 
@@ -96,7 +96,7 @@
   }
 
   @Override
-  public boolean isMaterializableInAllContexts(AppView<?> appView) {
+  public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) {
     DexType baseType = type.toBaseType(appView.dexItemFactory());
     if (baseType.isClassType()) {
       DexClass clazz = appView.definitionFor(type);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 7bccd30..14dee6f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -81,7 +81,7 @@
   public boolean isMaterializableInContext(
       AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     return AccessControl.isFieldAccessible(
-            appView.appInfo().resolveField(field),
+            appView.appInfo().resolveField(field).getResolvedField(),
             appView.definitionForHolder(field),
             context.getHolder(),
             appView.appInfo())
@@ -89,8 +89,8 @@
   }
 
   @Override
-  public boolean isMaterializableInAllContexts(AppView<?> appView) {
-    DexEncodedField encodedField = appView.appInfo().resolveField(field);
+  public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) {
+    DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
     if (encodedField == null) {
       assert false;
       return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 67a0858..e6b1ee6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -86,7 +86,7 @@
   }
 
   @Override
-  public boolean isMaterializableInAllContexts(AppView<?> appView) {
+  public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) {
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index 457d49b..ab7c218 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -87,7 +87,7 @@
   }
 
   @Override
-  public boolean isMaterializableInAllContexts(AppView<?> appView) {
+  public boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView) {
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
index f0425b0..3ce27a0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -43,7 +43,7 @@
   public abstract boolean isMaterializableInContext(
       AppView<AppInfoWithLiveness> appView, ProgramMethod context);
 
-  public abstract boolean isMaterializableInAllContexts(AppView<?> appView);
+  public abstract boolean isMaterializableInAllContexts(AppView<AppInfoWithLiveness> appView);
 
   @Override
   public abstract SingleValue rewrittenWithLens(
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
index 8cd1503..3cd25ea 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.cf.code.CfNop;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -61,7 +61,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return ConstraintWithTarget.ALWAYS;
   }
 
@@ -76,7 +76,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
index 2a32bb6..4b99975 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -62,7 +62,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forAlwaysMaterializingUser();
   }
 
@@ -77,7 +77,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index 2ace686..5b68bf5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -101,7 +102,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forArgument();
   }
 
@@ -136,7 +137,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 432cf11..845cb26 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -151,7 +152,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forArrayGet();
   }
 
@@ -237,7 +238,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return array() == value;
   }
 
@@ -263,7 +264,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index a1ed1d7..fcea78c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -74,7 +74,7 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     if (array().type.isNullable()) {
       return AbstractError.specific(appView.dexItemFactory().npeType);
     }
@@ -84,13 +84,13 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context).isThrowing();
   }
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
@@ -114,7 +114,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forArrayLength();
   }
 
@@ -140,7 +140,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return array() == value;
   }
 
@@ -155,7 +155,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 37daada..860473c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -134,7 +134,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     // In debug mode, ArrayPut has a side-effect on the locals.
     if (appView.options().debug) {
       return true;
@@ -192,7 +192,7 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
@@ -220,7 +220,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forArrayPut();
   }
 
@@ -240,7 +240,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return array() == value;
   }
 
@@ -260,7 +260,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index e5316e4..c6012bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Assume.Assumption;
@@ -238,7 +239,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forAssume();
   }
 
@@ -270,7 +271,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index f0d4eff..613ff5b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -123,7 +123,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forBinop();
   }
 
@@ -144,7 +144,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index da952bd..cdbf6f0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -96,12 +97,12 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context).isThrowing();
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     if (appView.options().debug || !appView.appInfo().hasLiveness()) {
       return AbstractError.top();
     }
@@ -154,8 +155,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forCheckCast(type, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forCheckCast(type, context.getHolder());
   }
 
   @Override
@@ -225,7 +226,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index c2d5232..759e764 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -22,6 +23,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public class ConstClass extends ConstInstruction {
 
@@ -99,7 +101,7 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     DexType baseType = getValue().toBaseType(appView.dexItemFactory());
     if (baseType.isPrimitiveType()) {
       return AbstractError.bottom();
@@ -108,7 +110,7 @@
     // Not applicable for D8.
     if (!appView.enableWholeProgramOptimizations()) {
       // Unless the type of interest is same as the context.
-      if (baseType == context) {
+      if (baseType == context.getHolderType()) {
         return AbstractError.bottom();
       }
       return AbstractError.top();
@@ -133,13 +135,13 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context).isThrowing();
   }
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
@@ -164,8 +166,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forConstClass(clazz, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forConstClass(clazz, context.getHolder());
   }
 
   @Override
@@ -189,7 +191,8 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (!instructionMayHaveSideEffects(appView, context)) {
       return appView.abstractValueFactory().createSingleConstClassValue(clazz);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
index 8db125c..0e8ce5d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 
@@ -50,7 +50,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forConstInstruction();
   }
 
@@ -60,7 +60,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index a390b3f..3973d6e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -78,7 +79,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forConstMethodHandle();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 774475d..6f153a6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -124,7 +125,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forConstMethodType();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index fcb0c1b..dc66e3c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
@@ -25,6 +26,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOutputMode;
 import com.android.tools.r8.utils.NumberUtils;
 import java.util.Set;
@@ -333,7 +335,8 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     return appView.abstractValueFactory().createSingleNumberValue(value);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index a90a67e..3bd1d56 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -17,6 +18,7 @@
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.io.UTFDataFormatException;
 
 public class ConstString extends ConstInstruction {
@@ -156,12 +158,13 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (!instructionInstanceCanThrow()) {
       return appView.abstractValueFactory().createSingleStringValue(value);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index 64e2c14..292d6b7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -66,7 +66,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forDebugLocalRead();
   }
 
@@ -88,7 +88,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index 5b4c66b..842eaa4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -99,7 +99,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forDebugLocalsChange();
   }
 
@@ -135,7 +135,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index b9ab9cd..fd6f9a9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.cf.code.CfNop;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -62,7 +62,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forDebugPosition();
   }
 
@@ -89,7 +89,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index bc29749..959dbfc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
@@ -157,12 +158,12 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forDexItemBasedConstString(item, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forDexItemBasedConstString(item, context.getHolder());
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index cddc41e..070b8e9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -94,7 +94,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forDup();
   }
 
@@ -119,7 +119,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index 465c408..7053383 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -108,7 +108,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forDup2();
   }
 
@@ -133,7 +133,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 888536f..436de09 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -3,15 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isMemberVisibleFromOriginalContext;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 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.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
@@ -61,56 +60,32 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     return instructionInstanceCanThrow(appView, context, SideEffectAssumption.NONE);
   }
 
   public AbstractError instructionInstanceCanThrow(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
-    DexEncodedField resolvedField;
-    if (appView.enableWholeProgramOptimizations()) {
-      // TODO(b/123857022): Should be possible to use definitionFor().
-      resolvedField = appView.appInfo().resolveField(field);
-    } else {
-      // In D8, only allow the field in the same context.
-      if (field.holder != context) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
+    SuccessfulFieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(field, context).asSuccessfulResolution();
+    if (resolutionResult == null) {
+      return AbstractError.top();
+    }
+    DexEncodedField resolvedField = resolutionResult.getResolvedField();
+    // Check if the instruction may fail with an IncompatibleClassChangeError.
+    if (resolvedField.isStatic() != isStaticFieldInstruction()) {
+      return AbstractError.top();
+    }
+    // Check if the resolution target is accessible.
+    if (resolutionResult.getResolvedHolder() != context.getHolder()) {
+      if (resolutionResult
+          .isAccessibleFrom(context, appView.appInfo().withClassHierarchy())
+          .isPossiblyFalse()) {
         return AbstractError.top();
       }
-      // Note that, in D8, we are not using AppInfo#resolveField to avoid traversing the hierarchy.
-      DexClass holder = appView.definitionFor(field.holder);
-      if (holder == null) {
-        return AbstractError.top();
-      }
-      resolvedField = holder.lookupField(field);
-    }
-    // * NoSuchFieldError (resolution failure).
-    if (resolvedField == null) {
-      if (appView.enableWholeProgramOptimizations()) {
-        return AbstractError.specific(appView.dexItemFactory().noSuchFieldErrorType);
-      } else {
-        // In D8, the field lookup can only consult the context definition. Nothing can be concluded
-        // from a lookup failure. For example, it could be ICCE or IAE if the current field access
-        // is referring to incompatible or invisible field in a super type, respectively.
-        return AbstractError.top();
-      }
-    }
-    // * IncompatibleClassChangeError (instance-* for static field and vice versa).
-    if (resolvedField.isStaticMember()) {
-      if (isInstanceGet() || isInstancePut()) {
-        return AbstractError.specific(appView.dexItemFactory().icceType);
-      }
-    } else {
-      if (isStaticGet() || isStaticPut()) {
-        return AbstractError.specific(appView.dexItemFactory().icceType);
-      }
-    }
-    // * IllegalAccessError (not visible from the access context).
-    if (!isMemberVisibleFromOriginalContext(
-        appView, context, field.holder, resolvedField.accessFlags)) {
-      return AbstractError.specific(appView.dexItemFactory().illegalAccessErrorType);
     }
     // TODO(b/137168535): Without non-null tracking, only locally created receiver is allowed in D8.
-    // * NullPointerException (null receiver).
+    // Check if the instruction may fail with a NullPointerException (null receiver).
     if (isInstanceGet() || isInstancePut()) {
       if (!assumption.canAssumeReceiverIsNotNull()) {
         Value receiver = inValues.get(0);
@@ -138,7 +113,7 @@
       if (field.holder.classInitializationMayHaveSideEffects(
           appView,
           // Types that are a super type of `context` are guaranteed to be initialized already.
-          type -> appView.isSubtype(context, type).isTrue(),
+          type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
           Sets.newIdentityHashSet())) {
         return AbstractError.top();
       }
@@ -153,7 +128,7 @@
   }
 
   @Override
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (instructionMayTriggerMethodInvocation(appView, context)) {
       // This may trigger class initialization, which could potentially read any field.
       return UnknownFieldSet.getInstance();
@@ -163,7 +138,7 @@
       DexField field = getField();
       DexEncodedField encodedField = null;
       if (appView.enableWholeProgramOptimizations()) {
-        encodedField = appView.appInfo().resolveField(field);
+        encodedField = appView.appInfo().resolveField(field).getResolvedField();
       } else {
         DexClass clazz = appView.definitionFor(field.holder);
         if (clazz != null) {
@@ -219,7 +194,7 @@
       DexItemFactory dexItemFactory = appView.dexItemFactory();
       DexEncodedMethod resolutionResult =
           appInfo
-              .resolveMethod(clazz.type, dexItemFactory.objectMembers.finalize)
+              .resolveMethodOnClass(dexItemFactory.objectMembers.finalize, clazz)
               .getSingleTarget();
       return resolutionResult != null && resolutionResult.isProgramMethod(appView);
     }
@@ -228,9 +203,10 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     assert isFieldGet();
-    DexEncodedField field = appView.appInfo().resolveField(getField());
+    DexEncodedField field = appView.appInfo().resolveField(getField()).getResolvedField();
     if (field != null) {
       return field.getOptimizationInfo().getAbstractValue();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
index 107e06e..e06cdbee 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -44,11 +45,14 @@
    * does not guarantee that the assignments found dominate all the normal exits.
    */
   public static Map<DexEncodedField, StaticPut> findUniqueStaticPuts(
-      AppView<?> appView, IRCode code, Set<DexEncodedField> fields) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      IRCode code,
+      Set<DexEncodedField> fields) {
     Set<DexEncodedField> writtenMoreThanOnce = Sets.newIdentityHashSet();
     Map<DexEncodedField, StaticPut> uniqueStaticPuts = new IdentityHashMap<>();
     for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) {
-      DexEncodedField field = appView.appInfo().resolveField(staticPut.getField());
+      DexEncodedField field =
+          appView.appInfo().resolveField(staticPut.getField()).getResolvedField();
       if (field == null || !fields.contains(field) || writtenMoreThanOnce.contains(field)) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index 02ee0eb..3ff5c53 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 
 public class InitClass extends Instruction {
@@ -78,8 +80,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInitClass(
@@ -92,14 +94,14 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     if (!isTypeVisibleFromContext(appView, context, clazz)) {
       return AbstractError.top();
     }
     if (clazz.classInitializationMayHaveSideEffects(
         appView,
         // Types that are a super type of `context` are guaranteed to be initialized already.
-        type -> appView.isSubtype(context, type).isTrue(),
+        type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
         Sets.newIdentityHashSet())) {
       return AbstractError.top();
     }
@@ -108,24 +110,24 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context).isThrowing();
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     if (appView.enableWholeProgramOptimizations()) {
       // In R8, check if the class initialization of `clazz` or any of its ancestor types may have
       // side effects.
       return clazz.classInitializationMayHaveSideEffects(
           appView,
           // Types that are a super type of `context` are guaranteed to be initialized already.
-          type -> appView.isSubtype(context, type).isTrue(),
+          type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
           Sets.newIdentityHashSet());
     } else {
       // In D8, this instruction may trigger class initialization if `clazz` is different from the
       // current context.
-      return clazz != context;
+      return clazz != context.getHolderType();
     }
   }
 
@@ -146,8 +148,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInitClass(clazz, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInitClass(clazz, context.getHolder());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
index e78d3a7..9077c97 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
 
 public interface InstanceFieldInstruction {
@@ -17,7 +17,7 @@
   Value object();
 
   boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption);
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption);
 
   FieldInstruction asFieldInstruction();
 
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 3050da7..38c1813 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
@@ -19,6 +19,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.graph.ProgramMethod;
 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;
@@ -28,6 +29,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Set;
 
 public class InstanceGet extends FieldInstruction implements InstanceFieldInstruction {
@@ -116,7 +118,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
   }
 
@@ -127,7 +129,7 @@
     // * IncompatibleClassChangeError (instance-* instruction for static fields)
     // * IllegalAccessError (not visible from the access context)
     // * NullPointerException (null receiver)
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
@@ -151,8 +153,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInstanceGet(getField(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInstanceGet(getField(), context.getHolder());
   }
 
   @Override
@@ -204,7 +206,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return object() == value;
   }
 
@@ -221,8 +223,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInstanceGet(
@@ -230,7 +232,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index c18e3c5..3d7ae52 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -86,8 +87,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInstanceOf(type, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInstanceOf(type, context.getHolder());
   }
 
   @Override
@@ -117,7 +118,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
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 fd76ad3..d5aad2d 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
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 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;
@@ -115,7 +116,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (appView.appInfo().hasLiveness()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
@@ -124,7 +125,8 @@
         return true;
       }
 
-      DexEncodedField encodedField = appInfoWithLiveness.resolveField(getField());
+      DexEncodedField encodedField =
+          appInfoWithLiveness.resolveField(getField()).getResolvedField();
       assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
       return appInfoWithLiveness.isFieldRead(encodedField)
           || isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
@@ -142,7 +144,7 @@
     // * IllegalAccessError (not visible from the access context)
     // * NullPointerException (null receiver)
     // * not read at all
-    boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method().holder());
+    boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.context());
     assert appView.enableWholeProgramOptimizations() || haveSideEffects
         : "Expected instance-put instruction to have side effects in D8";
     return !haveSideEffects;
@@ -190,8 +192,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInstancePut(getField(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInstancePut(getField(), context.getHolder());
   }
 
   @Override
@@ -232,7 +234,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return object() == value;
   }
 
@@ -249,8 +251,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInstancePut(
@@ -258,7 +260,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index b280728..5fa8d11 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -30,6 +31,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
@@ -139,7 +141,8 @@
     return oldOutValue;
   }
 
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     assert hasOutValue();
     return UnknownValue.getInstance();
   }
@@ -532,7 +535,8 @@
     return false;
   }
 
-  public boolean isBlockLocalInstructionWithoutSideEffects(AppView<?> appView, DexType context) {
+  public boolean isBlockLocalInstructionWithoutSideEffects(
+      AppView<?> appView, ProgramMethod context) {
     return definesBlockLocalValue() && !instructionMayHaveSideEffects(appView, context);
   }
 
@@ -570,12 +574,12 @@
     return instructionTypeCanThrow();
   }
 
-  public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+  public boolean instructionMayHaveSideEffects(AppView<?> appView, ProgramMethod context) {
     return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
   }
 
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow();
   }
 
@@ -584,9 +588,9 @@
    * indirectly (e.g., via class initialization).
    */
   public abstract boolean instructionMayTriggerMethodInvocation(
-      AppView<?> appView, DexType context);
+      AppView<?> appView, ProgramMethod context);
 
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     return instructionInstanceCanThrow() ? AbstractError.top() : AbstractError.bottom();
   }
 
@@ -601,7 +605,7 @@
    * Returns an abstraction of the set of fields that may possibly be read as a result of executing
    * this instruction.
    */
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (instructionMayTriggerMethodInvocation(appView, context)
         && instructionMayHaveSideEffects(appView, context)) {
       return UnknownFieldSet.getInstance();
@@ -1378,7 +1382,7 @@
    * <p>The type is used to judge visibility constraints and also for dispatch decisions.
    */
   public abstract ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context);
 
   public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
 
@@ -1432,7 +1436,7 @@
    * @return true if the instruction throws NullPointerException if value is null at runtime, false
    *     otherwise.
    */
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
@@ -1460,8 +1464,8 @@
    */
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index c3fec64..29b8a3b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -165,7 +166,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forInvokeCustom();
   }
 
@@ -196,7 +197,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return true;
   }
 }
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 e492db1..095db29 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
@@ -13,6 +13,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.graph.ProgramMethod;
 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;
@@ -121,24 +122,25 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
+  public DexEncodedMethod lookupSingleTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver) {
     DexMethod invokedMethod = getInvokedMethod();
     if (appView.appInfo().hasLiveness()) {
       AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
-      DexEncodedMethod result = appInfo.lookupDirectTarget(invokedMethod, invocationContext);
+      DexEncodedMethod result = appInfo.lookupDirectTarget(invokedMethod, context);
       assert verifyD8LookupResult(
-          result, appView.appInfo().lookupDirectTargetOnItself(invokedMethod, invocationContext));
+          result, appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context));
       return result;
     }
     // In D8, we can treat invoke-direct instructions as having a single target if the invoke is
     // targeting a method in the enclosing class.
-    return appView.appInfo().lookupDirectTargetOnItself(invokedMethod, invocationContext);
+    return appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context);
   }
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeDirect(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeDirect(getInvokedMethod(), context.getHolder());
   }
 
   @Override
@@ -149,8 +151,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInvokeDirect(
@@ -159,7 +161,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (appView.options().debug) {
       return true;
     }
@@ -217,8 +219,8 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    DexEncodedMethod method = code.method();
-    if (instructionMayHaveSideEffects(appView, method.holder())) {
+    ProgramMethod context = code.context();
+    if (instructionMayHaveSideEffects(appView, context)) {
       return false;
     }
 
@@ -236,7 +238,7 @@
           if (appView.dexItemFactory().isConstructor(invoke.getInvokedMethod())
               && invoke.getReceiver() == getReceiver()) {
             // If another constructor call than `this` is found, then it must not have side effects.
-            if (invoke.instructionMayHaveSideEffects(appView, method.holder())) {
+            if (invoke.instructionMayHaveSideEffects(appView, context)) {
               return false;
             }
             if (otherInitCalls == null) {
@@ -267,7 +269,7 @@
   }
 
   @Override
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     DexMethod invokedMethod = getInvokedMethod();
 
     // Trivial instance initializers do not read any fields.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index e9f3063..f998009 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -9,6 +9,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.graph.ProgramMethod;
 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;
@@ -86,26 +87,28 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
+  public DexEncodedMethod lookupSingleTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver) {
     if (appView.appInfo().hasLiveness()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       return appViewWithLiveness
           .appInfo()
           .lookupSingleVirtualTarget(
               getInvokedMethod(),
-              invocationContext,
+              context,
               true,
               appView,
-              TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
-              getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
+              TypeAnalysis.getRefinedReceiverType(
+                  appViewWithLiveness, getInvokedMethod(), receiver),
+              receiver.getDynamicLowerBoundType(appViewWithLiveness));
     }
     return null;
   }
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeInterface(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeInterface(getInvokedMethod(), context.getHolder());
   }
 
   @Override
@@ -116,8 +119,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInvokeInterface(
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 6c9fb6f..8c059d9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -28,12 +28,9 @@
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.BitSet;
-import java.util.Collection;
 import java.util.List;
-import java.util.Set;
 
 public abstract class InvokeMethod extends Invoke {
 
@@ -76,23 +73,22 @@
   // In subclasses, e.g., invoke-virtual or invoke-super, use a narrower receiver type by using
   // receiver type and calling context---the holder of the method where the current invocation is.
   // TODO(b/140204899): Refactor lookup methods to be defined in a single place.
-  public abstract DexEncodedMethod lookupSingleTarget(
-      AppView<?> appView, DexType invocationContext);
+  public abstract DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context);
 
   public final ProgramMethod lookupSingleProgramTarget(AppView<?> appView, ProgramMethod context) {
-    DexEncodedMethod singleTarget = lookupSingleTarget(appView, context.getHolderType());
+    DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
     return singleTarget != null ? singleTarget.asProgramMethod(appView) : null;
   }
 
   // TODO(b/140204899): Refactor lookup methods to be defined in a single place.
-  public Collection<DexEncodedMethod> lookupTargets(
-      AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
+  public ProgramMethodSet lookupProgramDispatchTargets(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (!getInvokedMethod().holder.isClassType()) {
       return null;
     }
     if (!isInvokeMethodWithDynamicDispatch()) {
-      DexEncodedMethod singleTarget = lookupSingleTarget(appView, invocationContext);
-      return singleTarget != null ? SetUtils.newIdentityHashSet(singleTarget) : null;
+      ProgramMethod singleTarget = lookupSingleProgramTarget(appView, context);
+      return singleTarget != null ? ProgramMethodSet.create(singleTarget) : null;
     }
     DexProgramClass refinedReceiverUpperBound =
         asProgramClassOrNull(
@@ -113,27 +109,31 @@
         refinedReceiverLowerBound = null;
       }
     }
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult =
+        appView.appInfo().resolveMethod(method, isInvokeInterface());
     LookupResult lookupResult;
     if (refinedReceiverUpperBound != null) {
       lookupResult =
           resolutionResult.lookupVirtualDispatchTargets(
-              appView.definitionForProgramType(invocationContext),
+              context.getHolder(),
               appView.withLiveness().appInfo(),
               refinedReceiverUpperBound,
               refinedReceiverLowerBound);
     } else {
       lookupResult =
           resolutionResult.lookupVirtualDispatchTargets(
-              appView.definitionForProgramType(invocationContext),
-              appView.withLiveness().appInfo());
+              context.getHolder(), appView.withLiveness().appInfo());
     }
     if (lookupResult.isLookupResultFailure()) {
       return null;
     }
-    Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+    ProgramMethodSet result = ProgramMethodSet.create();
     lookupResult.forEach(
-        methodTarget -> result.add(methodTarget.getDefinition()),
+        methodTarget -> {
+          if (methodTarget.isProgramMethod()) {
+            result.add(methodTarget.asProgramMethod());
+          }
+        },
         lambda -> {
           // TODO(b/150277553): Support lambda targets.
         });
@@ -195,17 +195,18 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return true;
   }
 
   @Override
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     assert hasOutValue();
     DexEncodedMethod method = lookupSingleTarget(appView, context);
     if (method != null) {
@@ -224,7 +225,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
     if (singleTarget != null) {
       BitSet nonNullParamOrThrow = singleTarget.getOptimizationInfo().getNonNullParamOrThrow();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 00ffbe1..dbee028 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -3,8 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
+
 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.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -53,7 +56,20 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public final DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+    return lookupSingleTarget(appView, context, getReceiver());
+  }
+
+  public abstract DexEncodedMethod lookupSingleTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver);
+
+  public final ProgramMethod lookupSingleProgramTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver) {
+    return asProgramMethodOrNull(lookupSingleTarget(appView, context, receiver), appView);
+  }
+
+  @Override
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return value == getReceiver() || super.throwsNpeIfValueIsNull(value, appView, context);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 4006bd7..1e41503 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -77,8 +78,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeMultiNewArray(type, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeMultiNewArray(type, context.getHolder());
   }
 
   @Override
@@ -113,7 +114,7 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     DexType baseType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
     if (baseType.isPrimitiveType()) {
       // Primitives types are known to be present and accessible.
@@ -123,7 +124,7 @@
 
     assert baseType.isReferenceType();
 
-    if (baseType == context) {
+    if (baseType == context.getHolderType()) {
       // The enclosing type is known to be present and accessible.
       return instructionInstanceCanThrowNegativeArraySizeException();
     }
@@ -172,7 +173,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     // Check if the instruction has a side effect on the locals environment.
     if (hasOutValue() && outValue().hasLocalInfo()) {
       assert appView.options().debug;
@@ -184,11 +185,11 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 3f5a1ab..1d6547e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -106,8 +107,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeNewArray(type, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeNewArray(type, context.getHolder());
   }
 
   @Override
@@ -140,7 +141,7 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     DexType baseType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
     if (baseType.isPrimitiveType()) {
       // Primitives types are known to be present and accessible.
@@ -150,7 +151,7 @@
 
     assert baseType.isReferenceType();
 
-    if (baseType == context) {
+    if (baseType == context.getHolderType()) {
       // The enclosing type is known to be present and accessible.
       return AbstractError.bottom();
     }
@@ -185,7 +186,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     // Check if the instruction has a side effect on the locals environment.
     if (hasOutValue() && outValue().hasLocalInfo()) {
       assert appView.options().debug;
@@ -197,11 +198,11 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 8d46da1..6a1e8a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -23,7 +23,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Collection;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.List;
 
 public class InvokePolymorphic extends InvokeMethod {
@@ -120,22 +120,22 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
+  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
     // TODO(herhut): Implement lookup target for invokePolymorphic.
     return null;
   }
 
   @Override
-  public Collection<DexEncodedMethod> lookupTargets(
-      AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
+  public ProgramMethodSet lookupProgramDispatchTargets(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     // TODO(herhut): Implement lookup target for invokePolymorphic.
     return null;
   }
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokePolymorphic(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokePolymorphic(getInvokedMethod(), context.getHolder());
   }
 
   @Override
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 5ea6e46..2c5faaf 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
@@ -103,13 +103,13 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
+  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
     DexMethod invokedMethod = getInvokedMethod();
     if (appView.appInfo().hasLiveness()) {
       AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
-      DexEncodedMethod result = appInfo.lookupStaticTarget(invokedMethod, invocationContext);
+      DexEncodedMethod result = appInfo.lookupStaticTarget(invokedMethod, context);
       assert verifyD8LookupResult(
-          result, appView.appInfo().lookupStaticTargetOnItself(invokedMethod, invocationContext));
+          result, appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context));
       return result;
     }
     // Allow optimizing static library invokes in D8.
@@ -120,13 +120,13 @@
     }
     // In D8, we can treat invoke-static instructions as having a single target if the invoke is
     // targeting a method in the enclosing class.
-    return appView.appInfo().lookupStaticTargetOnItself(invokedMethod, invocationContext);
+    return appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context);
   }
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeStatic(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeStatic(getInvokedMethod(), context.getHolder());
   }
 
   @Override
@@ -148,8 +148,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInvokeStatic(
@@ -158,7 +158,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (!appView.enableWholeProgramOptimizations()) {
       return true;
     }
@@ -207,7 +207,7 @@
               appView,
               // Types that are a super type of `context` are guaranteed to be initialized
               // already.
-              type -> appView.isSubtype(context, type).isTrue(),
+              type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
               Sets.newIdentityHashSet());
     }
 
@@ -216,6 +216,6 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 }
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 588a0f1..e2b2e3a 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
@@ -9,6 +9,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.graph.ProgramMethod;
 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;
@@ -93,12 +94,13 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
-    if (appView.appInfo().hasLiveness() && invocationContext != null) {
+  public DexEncodedMethod lookupSingleTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver) {
+    if (appView.appInfo().hasLiveness() && context != null) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
-      if (appInfo.isSubtype(invocationContext, getInvokedMethod().holder)) {
-        return appInfo.lookupSuperTarget(getInvokedMethod(), invocationContext);
+      if (appInfo.isSubtype(context.getHolderType(), getInvokedMethod().holder)) {
+        return appInfo.lookupSuperTarget(getInvokedMethod(), context);
       }
     }
     return null;
@@ -106,15 +108,15 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeSuper(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeSuper(getInvokedMethod(), context.getHolder());
   }
 
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInvokeSuper(
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 b9a0765..69111fc 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
@@ -12,6 +12,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.graph.ProgramMethod;
 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;
@@ -90,19 +91,20 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
-    return lookupSingleTarget(appView, invocationContext, getInvokedMethod(), getReceiver());
+  public DexEncodedMethod lookupSingleTarget(
+      AppView<?> appView, ProgramMethod context, Value receiver) {
+    return lookupSingleTarget(appView, context, receiver, getInvokedMethod());
   }
 
   public static DexEncodedMethod lookupSingleTarget(
-      AppView<?> appView, DexType invocationContext, DexMethod method, Value receiver) {
+      AppView<?> appView, ProgramMethod context, Value receiver, DexMethod method) {
     if (appView.appInfo().hasLiveness()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       return appViewWithLiveness
           .appInfo()
           .lookupSingleVirtualTarget(
               method,
-              invocationContext,
+              context,
               false,
               appView,
               TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, method, receiver),
@@ -126,8 +128,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forInvokeVirtual(getInvokedMethod(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forInvokeVirtual(getInvokedMethod(), context.getHolder());
   }
 
   @Override
@@ -138,8 +140,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forInvokeVirtual(
@@ -148,7 +150,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (!appView.enableWholeProgramOptimizations()) {
       return true;
     }
@@ -205,6 +207,6 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
index 8e1e347..9536919 100644
--- a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import java.util.List;
@@ -48,7 +48,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forJumpInstruction();
   }
 
@@ -58,7 +58,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index 9886690..eee3bef 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -63,7 +64,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forLoad();
   }
 
@@ -99,7 +100,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 598bc6c..f779651 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.code.MonitorExit;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -109,7 +109,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forMonitor();
   }
 
@@ -141,7 +141,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return object() == value;
   }
 
@@ -156,7 +156,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index 24e831c..f531071 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -99,7 +99,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forMove();
   }
 
@@ -119,7 +119,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 9f21f17..4c87b70 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -87,7 +88,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forMoveException();
   }
 
@@ -121,7 +122,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index bd5c34f..c730efc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -107,8 +108,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forNewArrayEmpty(type, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forNewArrayEmpty(type, context.getHolder());
   }
 
   @Override
@@ -139,7 +140,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index fd6cf65..aaa27dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -101,7 +101,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forNewArrayFilledData();
   }
 
@@ -116,7 +116,7 @@
   }
 
   @Override
-  public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+  public AbstractError instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
     if (appView.options().debug) {
       return AbstractError.top();
     }
@@ -130,7 +130,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     // Treat the instruction as possibly having side-effects if it may throw or the array is used.
     if (instructionInstanceCanThrow(appView, context).isThrowing()
         || src().numberOfAllUsers() > 1) {
@@ -145,12 +145,12 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 }
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 e30cdf1..3e6c121 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
@@ -14,6 +14,7 @@
 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.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -24,6 +25,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 
 public class NewInstance extends Instruction {
@@ -100,8 +102,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forNewInstance(clazz, invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forNewInstance(clazz, context.getHolder());
   }
 
   @Override
@@ -132,8 +134,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forNewInstance(
@@ -142,13 +144,16 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     if (!appView.enableWholeProgramOptimizations()) {
       return !(dexItemFactory.libraryTypesAssumedToBePresent.contains(clazz)
           && dexItemFactory.libraryClassesWithoutStaticInitialization.contains(clazz));
     }
 
+    assert appView.appInfo().hasLiveness();
+    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+
     if (clazz.isPrimitiveType() || clazz.isArrayType()) {
       assert false : "Unexpected new-instance instruction with primitive or array type";
       return true;
@@ -165,9 +170,8 @@
     }
 
     // Verify that the instruction does not lead to an IllegalAccessError.
-    if (appView.appInfo().hasLiveness()
-        && !isMemberVisibleFromOriginalContext(
-            appView, context, definition.type, definition.accessFlags)) {
+    if (!isMemberVisibleFromOriginalContext(
+        appView, context, definition.type, definition.accessFlags)) {
       return true;
     }
 
@@ -175,14 +179,16 @@
     if (definition.classInitializationMayHaveSideEffects(
         appView,
         // Types that are a super type of `context` are guaranteed to be initialized already.
-        type -> appView.isSubtype(context, type).isTrue(),
+        type -> appViewWithLiveness.appInfo().isSubtype(context.getHolderType(), type),
         Sets.newIdentityHashSet())) {
       return true;
     }
 
     // Verify that the object does not have a finalizer.
     ResolutionResult finalizeResolutionResult =
-        appView.appInfo().resolveMethod(clazz, dexItemFactory.objectMembers.finalize);
+        appViewWithLiveness
+            .appInfo()
+            .resolveMethodOnClass(dexItemFactory.objectMembers.finalize, clazz);
     if (finalizeResolutionResult.isSingleResolution()) {
       DexMethod finalizeMethod = finalizeResolutionResult.getSingleTarget().method;
       if (finalizeMethod != dexItemFactory.enumMethods.finalize
@@ -196,7 +202,7 @@
 
   @Override
   public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   public void markNoSpilling() {
@@ -208,19 +214,19 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     if (appView.enableWholeProgramOptimizations()) {
       // In R8, check if the class initialization of the holder or any of its ancestor types may
       // have side effects.
       return clazz.classInitializationMayHaveSideEffects(
           appView,
           // Types that are a super type of `context` are guaranteed to be initialized already.
-          type -> appView.isSubtype(context, type).isTrue(),
+          type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
           Sets.newIdentityHashSet());
     } else {
       // In D8, this instruction may trigger class initialization if the holder of the field is
       // different from the current context.
-      return clazz != context;
+      return clazz != context.getHolderType();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index ab07480..0124c45 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -66,7 +66,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forPop();
   }
 
@@ -97,7 +97,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 0adf292..19c9887 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.code.ReturnWide;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -110,7 +110,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forReturn();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
index 286b470..9b0e28a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
 
 public interface StaticFieldInstruction {
@@ -14,10 +14,10 @@
 
   Value outValue();
 
-  boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context);
+  boolean instructionMayHaveSideEffects(AppView<?> appView, ProgramMethod context);
 
   boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption);
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption);
 
   FieldInstruction asFieldInstruction();
 
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 de66cba..e0e8b63 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
@@ -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.graph.ProgramMethod;
 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;
@@ -27,6 +28,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
@@ -136,13 +138,13 @@
   }
 
   @Override
-  public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+  public boolean instructionMayHaveSideEffects(AppView<?> appView, ProgramMethod context) {
     return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
   }
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
   }
 
@@ -153,7 +155,7 @@
     // * IncompatibleClassChangeError (static-* instruction for instance fields)
     // * IllegalAccessError (not visible from the access context)
     // * side-effects in <clinit>
-    return !instructionMayHaveSideEffects(appView, code.method().holder());
+    return !instructionMayHaveSideEffects(appView, code.context());
   }
 
   @Override
@@ -177,8 +179,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forStaticGet(getField(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forStaticGet(getField(), context.getHolder());
   }
 
   @Override
@@ -226,8 +228,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forStaticGet(
@@ -240,7 +242,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     DexType holder = getField().holder;
     if (appView.enableWholeProgramOptimizations()) {
       // In R8, check if the class initialization of the holder or any of its ancestor types may
@@ -248,12 +250,12 @@
       return holder.classInitializationMayHaveSideEffects(
           appView,
           // Types that are a super type of `context` are guaranteed to be initialized already.
-          type -> appView.isSubtype(context, type).isTrue(),
+          type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
           Sets.newIdentityHashSet());
     } else {
       // In D8, this instruction may trigger class initialization if the holder of the field is
       // different from the current context.
-      return holder != context;
+      return holder != context.getHolderType();
     }
   }
 }
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 78d4de9..11732a9 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.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 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;
@@ -95,7 +96,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (appView.appInfo().hasLiveness()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
@@ -113,7 +114,8 @@
         return true;
       }
 
-      DexEncodedField encodedField = appInfoWithLiveness.resolveField(getField());
+      DexEncodedField encodedField =
+          appInfoWithLiveness.resolveField(getField()).getResolvedField();
       assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
 
       boolean isDeadProtoExtensionField =
@@ -139,7 +141,7 @@
     // * IllegalAccessError (not visible from the access context)
     // * side-effects in <clinit>
     // * not read _globally_
-    boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method().holder());
+    boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.context());
     assert appView.enableWholeProgramOptimizations() || haveSideEffects
         : "Expected static-put instruction to have side effects in D8";
     return !haveSideEffects;
@@ -187,8 +189,8 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
-    return inliningConstraints.forStaticPut(getField(), invocationContext);
+      InliningConstraints inliningConstraints, ProgramMethod context) {
+    return inliningConstraints.forStaticPut(getField(), context.getHolder());
   }
 
   @Override
@@ -226,8 +228,8 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
-      AppView<?> appView,
+      ProgramMethod context,
+      AppView<AppInfoWithLiveness> appView,
       Query mode,
       AnalysisAssumption assumption) {
     return ClassInitializationAnalysis.InstructionUtils.forStaticPut(
@@ -235,7 +237,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     DexType holder = getField().holder;
     if (appView.enableWholeProgramOptimizations()) {
       // In R8, check if the class initialization of the holder or any of its ancestor types may
@@ -243,12 +245,12 @@
       return holder.classInitializationMayHaveSideEffects(
           appView,
           // Types that are a super type of `context` are guaranteed to be initialized already.
-          type -> appView.isSubtype(context, type).isTrue(),
+          type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
           Sets.newIdentityHashSet());
     } else {
       // In D8, this instruction may trigger class initialization if the holder of the field is
       // different from the current context.
-      return holder != context;
+      return holder != context.getHolderType();
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 4fe63c0..ccd57ea 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -64,7 +65,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forStore();
   }
 
@@ -111,7 +112,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 2515934..5dfa623 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -89,7 +89,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forSwap();
   }
 
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 2b6b80d..6f47788 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.cf.code.CfThrow;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -72,7 +72,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forThrow();
   }
 
@@ -87,7 +87,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     if (exception() == value) {
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
index 1765626..9c65c4e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Unop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
@@ -47,7 +47,7 @@
 
   @Override
   public ConstraintWithTarget inliningConstraint(
-      InliningConstraints inliningConstraints, DexType invocationContext) {
+      InliningConstraints inliningConstraints, ProgramMethod context) {
     return inliningConstraints.forUnop();
   }
 
@@ -68,7 +68,7 @@
   }
 
   @Override
-  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+  public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
 }
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 bbe7f7f..02ab2d9 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
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -891,7 +892,7 @@
     return definition.isOutConstant() && !hasLocalInfo();
   }
 
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> appView, ProgramMethod context) {
     if (!appView.enableWholeProgramOptimizations()) {
       return UnknownValue.getInstance();
     }
@@ -901,7 +902,7 @@
       return UnknownValue.getInstance();
     }
 
-    return root.definition.getAbstractValue(appView, context);
+    return root.definition.getAbstractValue(appView.withLiveness(), context);
   }
 
   public boolean isDefinedByInstructionSatisfying(Predicate<Instruction> predicate) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index e6bd8bf..9f57ac1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -165,25 +165,25 @@
     }
 
     private void processInvoke(Invoke.Type originalType, DexMethod originalMethod) {
-      ProgramMethod source = currentMethod.getProgramMethod();
-      DexMethod context = source.getReference();
+      ProgramMethod context = currentMethod.getProgramMethod();
       GraphLenseLookupResult result =
-          appView.graphLense().lookupMethod(originalMethod, context, originalType);
+          appView.graphLense().lookupMethod(originalMethod, context.getReference(), originalType);
       DexMethod method = result.getMethod();
       Invoke.Type type = result.getType();
       if (type == Invoke.Type.INTERFACE || type == Invoke.Type.VIRTUAL) {
         // For virtual and interface calls add all potential targets that could be called.
-        ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+        ResolutionResult resolutionResult =
+            appView.appInfo().resolveMethod(method, type == Invoke.Type.INTERFACE);
         DexEncodedMethod target = resolutionResult.getSingleTarget();
         if (target != null) {
-          processInvokeWithDynamicDispatch(type, target, context.holder);
+          processInvokeWithDynamicDispatch(type, target, context);
         }
       } else {
         ProgramMethod singleTarget =
-            appView.appInfo().lookupSingleProgramTarget(type, method, context.holder, appView);
+            appView.appInfo().lookupSingleProgramTarget(type, method, context, appView);
         if (singleTarget != null) {
-          assert !source.getDefinition().isBridge()
-              || singleTarget.getDefinition() != source.getDefinition();
+          assert !context.getDefinition().isBridge()
+              || singleTarget.getDefinition() != context.getDefinition();
           // For static invokes, the class could be initialized.
           if (type == Invoke.Type.STATIC) {
             addClassInitializerTarget(singleTarget.getHolder());
@@ -194,7 +194,7 @@
     }
 
     private void processInvokeWithDynamicDispatch(
-        Invoke.Type type, DexEncodedMethod encodedTarget, DexType context) {
+        Invoke.Type type, DexEncodedMethod encodedTarget, ProgramMethod context) {
       DexMethod target = encodedTarget.method;
       DexClass clazz = appView.definitionFor(target.holder);
       if (clazz == null) {
@@ -214,12 +214,11 @@
           possibleProgramTargetsCache.computeIfAbsent(
               target,
               method -> {
-                ResolutionResult resolution =
-                    appView.appInfo().resolveMethod(method.holder, method, isInterface);
+                ResolutionResult resolution = appView.appInfo().resolveMethod(method, isInterface);
                 if (resolution.isVirtualTarget()) {
                   LookupResult lookupResult =
                       resolution.lookupVirtualDispatchTargets(
-                          appView.definitionForProgramType(context), appView.appInfo());
+                          context.getHolder(), appView.appInfo());
                   if (lookupResult.isLookupResultSuccess()) {
                     ProgramMethodSet targets = ProgramMethodSet.create();
                     lookupResult
@@ -258,7 +257,7 @@
         return;
       }
 
-      DexEncodedField encodedField = appView.appInfo().resolveField(field);
+      DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
       if (encodedField == null || appView.appInfo().isPinned(encodedField.field)) {
         return;
       }
@@ -283,7 +282,7 @@
 
     private void processFieldWrite(DexField field) {
       if (field.holder.isClassType()) {
-        DexEncodedField encodedField = appView.appInfo().resolveField(field);
+        DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
         if (encodedField != null && encodedField.isStatic()) {
           // Each static field access implicitly triggers the class initializer.
           addClassInitializerTarget(field.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 74f24c8..00eb737 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -183,7 +183,8 @@
   }
 
   public DexField resolveField(DexField field) {
-    DexEncodedField resolvedField = appView.appInfo().resolveField(field);
+    DexEncodedField resolvedField =
+        appView.appInfoForDesugaring().resolveField(field).getResolvedField();
     return resolvedField == null ? field : resolvedField.field;
   }
 
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 31fd3c0..14a936d 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
@@ -290,13 +290,13 @@
     if (options.testing.forceAssumeNoneInsertion) {
       assumers.add(new AliasIntroducer(appView));
     }
-    if (options.enableNonNullTracking) {
-      assumers.add(new NonNullTracker(appView));
-    }
     if (appView.enableWholeProgramOptimizations()) {
       assert appView.appInfo().hasLiveness();
       assert appView.rootSet() != null;
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      if (options.enableNonNullTracking) {
+        assumers.add(new NonNullTracker(appViewWithLiveness));
+      }
       this.classInliner =
           options.enableClassInlining && options.enableInlining ? new ClassInliner() : null;
       this.classStaticizer =
@@ -1164,7 +1164,7 @@
     } else {
       if (lambdaRewriter != null) {
         timing.begin("Desugar lambdas");
-        lambdaRewriter.desugarLambdas(method, code);
+        lambdaRewriter.desugarLambdas(code);
         timing.end();
         assert code.isConsistentSSA();
       }
@@ -1284,7 +1284,7 @@
     if (devirtualizer != null) {
       assert code.verifyTypes(appView);
       timing.begin("Devirtualize invoke interface");
-      devirtualizer.devirtualizeInvokeInterface(code, holder);
+      devirtualizer.devirtualizeInvokeInterface(code);
       timing.end();
     }
 
@@ -1372,7 +1372,7 @@
 
     timing.begin("Optimize class initializers");
     ClassInitializerDefaultsResult classInitializerDefaultsResult =
-        classInitializerDefaultsOptimization.optimize(method, code, feedback);
+        classInitializerDefaultsOptimization.optimize(code, feedback);
     timing.end();
 
     if (Log.ENABLED) {
@@ -1585,6 +1585,11 @@
       timing.end();
     }
 
+    if (appView.isCfByteCodePassThrough(method)) {
+      // If the code is pass trough, do not finalize by overwriting the existing code.
+      return timing;
+    }
+
     printMethod(code, "Optimized IR (SSA)", previous);
     timing.begin("Finalize IR");
     finalizeIR(code, feedback, timing);
@@ -1632,11 +1637,11 @@
     if (method.isInitializer()) {
       if (method.isClassInitializer()) {
         StaticFieldValueAnalysis.run(
-            appView, code, classInitializerDefaultsResult, feedback, code.method(), timing);
+            appView, code, classInitializerDefaultsResult, feedback, timing);
       } else {
         instanceFieldInitializationInfos =
             InstanceFieldValueAnalysis.run(
-                appView, code, classInitializerDefaultsResult, feedback, code.method(), timing);
+                appView, code, classInitializerDefaultsResult, feedback, timing);
       }
     }
     methodOptimizationInfoCollector.collectMethodOptimizationInfo(
@@ -1711,7 +1716,7 @@
     ProgramMethod method = code.context();
     ConstraintWithTarget state =
         shouldComputeInliningConstraint(method)
-            ? inliner.computeInliningConstraint(code, method)
+            ? inliner.computeInliningConstraint(code)
             : ConstraintWithTarget.NEVER;
     feedback.markProcessed(method.getDefinition(), state);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index e9aed7f..e059db8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
@@ -108,7 +107,7 @@
       if (androidApp != null) {
         DexApplication app =
             new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
-        appInfo = new AppInfoWithClassHierarchy(app);
+        appInfo = new AppInfo(app);
       }
       AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
       BackportedMethodRewriter.RewritableMethods rewritableMethods =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 47ee95f..72f630b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -171,7 +171,7 @@
     }
   }
 
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
   private final InterfaceMethodRewriter rewriter;
   private final Consumer<ProgramMethod> newSynthesizedMethodConsumer;
@@ -192,7 +192,7 @@
       new IdentityHashMap<>();
 
   ClassProcessor(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
+      AppView<?> appView,
       InterfaceMethodRewriter rewriter,
       Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
     this.appView = appView;
@@ -283,14 +283,14 @@
     // Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype
     // might have failed which is hidden by the insertion of the forward method). However, not doing
     // so could cause valid dispatches to become invalid by resolving to private overrides.
+    AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     DexClassAndMethod virtualDispatchTarget =
-        appView
-            .appInfo()
+        appInfo
             .resolveMethodOnInterface(method.holder, method)
-            .lookupVirtualDispatchTarget(clazz, appView.appInfo());
+            .lookupVirtualDispatchTarget(clazz, appInfo);
     if (virtualDispatchTarget == null) {
       // If no target is found due to multiple default method targets, preserve ICCE behavior.
-      ResolutionResult resolutionFromSubclass = appView.appInfo().resolveMethod(clazz, method);
+      ResolutionResult resolutionFromSubclass = appInfo.resolveMethodOn(clazz, method);
       if (resolutionFromSubclass.isIncompatibleClassChangeErrorResult()) {
         addICCEThrowingMethod(method, clazz);
         return;
@@ -327,8 +327,7 @@
     // If target is a non-interface library class it may be an emulated interface.
     if (!libraryHolder.isInterface()) {
       // Here we use step-3 of resolution to find a maximally specific default interface method.
-      DexClassAndMethod result =
-          appView.appInfo().lookupMaximallySpecificMethod(libraryHolder, method);
+      DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(libraryHolder, method);
       if (result != null && rewriter.isEmulatedInterface(result.getHolder().type)) {
         addForward.accept(result.getHolder(), result.getDefinition());
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index a640fb6..408b152 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -49,7 +48,7 @@
   public static final String DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX =
       "$r8$retargetLibraryMember$virtualDispatch";
 
-  private final AppView<AppInfoWithClassHierarchy> appView;
+  private final AppView<?> appView;
   private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
   // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
   // rewritten while the holder is non final but no superclass implement the method. In this case
@@ -59,9 +58,7 @@
   private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
 
   public DesugaredLibraryRetargeter(AppView<?> appView) {
-    assert appView.appInfo().hasClassHierarchy()
-        : "Class hierarchy required for desugared library.";
-    this.appView = appView.withClassHierarchy();
+    this.appView = appView;
     if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
       return;
     }
@@ -116,8 +113,8 @@
         // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
         ResolutionResult resolutionResult =
             appView
-                .appInfo()
-                .resolveMethod(invoke.getInvokedMethod().holder, invoke.getInvokedMethod());
+                .appInfoForDesugaring()
+                .resolveMethod(invoke.getInvokedMethod(), invoke.isInvokeInterface());
         if (resolutionResult.isFailedResolution()) {
           continue;
         }
@@ -134,9 +131,8 @@
       if (invoke.isInvokeSuper() && matchesVirtualRewrite(invoke.getInvokedMethod())) {
         DexEncodedMethod dexEncodedMethod =
             appView
-                .appInfo()
-                .withClassHierarchy()
-                .lookupSuperTarget(invoke.getInvokedMethod(), code.method().holder());
+                .appInfoForDesugaring()
+                .lookupSuperTarget(invoke.getInvokedMethod(), code.context());
         // Final methods can be rewritten as a normal invoke.
         if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
           DexMethod retargetMethod =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index e3c15ae..5fbf3df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -99,7 +99,8 @@
   public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
 
   private final AppView<?> appView;
-  private final DexString dexWrapperPrefix;
+  private final String dexWrapperPrefixString;
+  private final DexString dexWrapperPrefixDexString;
   private final Map<DexType, DexType> typeWrappers = new ConcurrentHashMap<>();
   private final Map<DexType, DexType> vivifiedTypeWrappers = new ConcurrentHashMap<>();
   // The invalidWrappers are wrappers with incorrect behavior because of final methods that could
@@ -114,13 +115,14 @@
   DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
-    this.dexWrapperPrefix = factory.createString("L" + WRAPPER_PREFIX);
+    dexWrapperPrefixString = "L" + appView.options().synthesizedClassPrefix + WRAPPER_PREFIX;
+    dexWrapperPrefixDexString = factory.createString(dexWrapperPrefixString);
     this.converter = converter;
     this.vivifiedSourceFile = appView.dexItemFactory().createString("vivified");
   }
 
   boolean hasSynthesized(DexType type) {
-    return type.descriptor.startsWith(dexWrapperPrefix);
+    return type.descriptor.startsWith(dexWrapperPrefixDexString);
   }
 
   boolean canGenerateWrapper(DexType type) {
@@ -145,12 +147,7 @@
 
   private DexType createWrapperType(DexType type, String suffix) {
     return factory.createType(
-        "L"
-            + appView.options().synthesizedClassPrefix
-            + WRAPPER_PREFIX
-            + type.toString().replace('.', '$')
-            + suffix
-            + ";");
+        dexWrapperPrefixString + type.toString().replace('.', '$') + suffix + ";");
   }
 
   private DexType getWrapper(DexType type, String suffix, Map<DexType, DexType> wrappers) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 5d494bb..1f6af29 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -102,7 +101,7 @@
   public static final String DEFAULT_METHOD_PREFIX = "$default$";
   public static final String PRIVATE_METHOD_PREFIX = "$private$";
 
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final AppView<?> appView;
   private final IRConverter converter;
   private final InternalOptions options;
   final DexItemFactory factory;
@@ -137,9 +136,7 @@
 
   public InterfaceMethodRewriter(AppView<?> appView, IRConverter converter) {
     assert converter != null;
-    assert appView.appInfo().hasClassHierarchy()
-        : "Cannot desugar interfaces without class hierarchy";
-    this.appView = appView.withClassHierarchy();
+    this.appView = appView;
     this.converter = converter;
     this.options = appView.options();
     this.factory = appView.dexItemFactory();
@@ -316,8 +313,8 @@
               // If it resolves to a program overrides, the invoke-super can remain.
               DexEncodedMethod dexEncodedMethod =
                   appView
-                      .appInfo()
-                      .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method().holder());
+                      .appInfoForDesugaring()
+                      .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.context());
               if (dexEncodedMethod != null) {
                 DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder());
                 if (dexClass != null && dexClass.isLibraryClass()) {
@@ -390,7 +387,7 @@
             } else {
               // The method can be a default method in the interface hierarchy.
               DexClassAndMethod virtualTarget =
-                  appView.appInfo().lookupMaximallySpecificMethod(clazz, method);
+                  appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, method);
               if (virtualTarget != null) {
                 // This is a invoke-direct call to a virtual method.
                 instructions.replaceCurrentInstruction(
@@ -448,7 +445,7 @@
     }
     if (singleTarget == null) {
       DexClassAndMethod result =
-          appView.appInfo().lookupMaximallySpecificMethod(dexClass, invokedMethod);
+          appView.appInfoForDesugaring().lookupMaximallySpecificMethod(dexClass, invokedMethod);
       if (result != null) {
         singleTarget = result.getDefinition();
       }
@@ -1123,7 +1120,7 @@
     DexMethod method = appView.graphLense().getOriginalMethodSignature(referencedFrom);
     Origin origin = getMethodOrigin(method);
     MethodPosition position = new MethodPosition(method);
-    options.warningMissingTypeForDesugar(origin, position, missing, method.holder);
+    options.warningMissingTypeForDesugar(origin, position, missing, method);
   }
 
   private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 05c9c66..7ed2773 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
@@ -61,6 +62,7 @@
  */
 public final class LambdaClass {
 
+  final AppView<?> appView;
   final LambdaRewriter rewriter;
   public final DexType type;
   public LambdaDescriptor descriptor;
@@ -74,19 +76,21 @@
       Suppliers.memoize(this::synthesizeLambdaClass); // NOTE: thread-safe.
 
   LambdaClass(
+      AppView<?> appView,
       LambdaRewriter rewriter,
-      DexType accessedFrom,
+      ProgramMethod accessedFrom,
       DexType lambdaClassType,
       LambdaDescriptor descriptor) {
     assert rewriter != null;
     assert lambdaClassType != null;
     assert descriptor != null;
 
+    this.appView = appView;
     this.rewriter = rewriter;
     this.type = lambdaClassType;
     this.descriptor = descriptor;
 
-    DexItemFactory factory = rewriter.getFactory();
+    DexItemFactory factory = appView.dexItemFactory();
     DexProto constructorProto = factory.createProto(
         factory.voidType, descriptor.captures.values);
     this.constructor =
@@ -107,17 +111,12 @@
   }
 
   // Generate unique lambda class type for lambda descriptor and instantiation point context.
-  static DexType createLambdaClassType(
-      LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
-    return createLambdaClassType(rewriter.getFactory(), accessedFrom, match);
-  }
-
   public static DexType createLambdaClassType(
-      DexItemFactory factory, DexType accessedFrom, LambdaDescriptor match) {
+      AppView<?> appView, ProgramMethod accessedFrom, LambdaDescriptor match) {
     StringBuilder lambdaClassDescriptor = new StringBuilder("L");
 
     // We always create lambda class in the same package where it is referenced.
-    String packageDescriptor = accessedFrom.getPackageDescriptor();
+    String packageDescriptor = accessedFrom.getHolderType().getPackageDescriptor();
     if (!packageDescriptor.isEmpty()) {
       lambdaClassDescriptor.append(packageDescriptor).append('/');
     }
@@ -129,12 +128,12 @@
     // just add the name of this type to make lambda class name unique.
     // It also helps link the class lambda originated from in some cases.
     if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
-      lambdaClassDescriptor.append(accessedFrom.getName()).append('$');
+      lambdaClassDescriptor.append(accessedFrom.getHolderType().getName()).append('$');
     }
 
     // Add unique lambda descriptor id
     lambdaClassDescriptor.append(match.uniqueId).append(';');
-    return factory.createType(lambdaClassDescriptor.toString());
+    return appView.dexItemFactory().createType(lambdaClassDescriptor.toString());
   }
 
   public final DexProgramClass getOrCreateLambdaClass() {
@@ -143,7 +142,7 @@
 
   private DexProgramClass synthesizeLambdaClass() {
     DexMethod mainMethod =
-        rewriter.getFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
+        appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
 
     DexProgramClass clazz =
         new DexProgramClass(
@@ -154,9 +153,9 @@
             // classloader (package private access is not allowed across classloaders, b/72538146).
             ClassAccessFlags.fromDexAccessFlags(
                 Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
-            rewriter.getFactory().objectType,
+            appView.dexItemFactory().objectType,
             buildInterfaces(),
-            rewriter.getFactory().createString("lambda"),
+            appView.dexItemFactory().createString("lambda"),
             null,
             Collections.emptyList(),
             null,
@@ -166,9 +165,9 @@
             synthesizeInstanceFields(),
             synthesizeDirectMethods(),
             synthesizeVirtualMethods(mainMethod),
-            rewriter.getFactory().getSkipNameValidationForTesting(),
+            appView.dexItemFactory().getSkipNameValidationForTesting(),
             LambdaClass::computeChecksumForSynthesizedClass);
-    rewriter.getAppInfo().addSynthesizedClass(clazz);
+    appView.appInfo().addSynthesizedClass(clazz);
 
     // The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
     // ModificationException we must use synchronization.
@@ -197,12 +196,12 @@
   }
 
   final DexField getCaptureField(int index) {
-    return rewriter
-        .getFactory()
+    return appView
+        .dexItemFactory()
         .createField(
             this.type,
             descriptor.captures.values[index],
-            rewriter.getFactory().createString("f$" + index));
+            appView.dexItemFactory().createString("f$" + index));
   }
 
   public final boolean isStateless() {
@@ -239,7 +238,7 @@
     // Synthesize bridge methods.
     for (DexProto bridgeProto : descriptor.bridges) {
       DexMethod bridgeMethod =
-          rewriter.getFactory().createMethod(type, bridgeProto, descriptor.name);
+          appView.dexItemFactory().createMethod(type, bridgeProto, descriptor.name);
       methods[index++] =
           new DexEncodedMethod(
               bridgeMethod,
@@ -337,7 +336,7 @@
 
   // Creates a delegation target for this particular lambda class. Note that we
   // should always be able to create targets for the lambdas we support.
-  private Target createTarget(DexType accessedFrom) {
+  private Target createTarget(ProgramMethod accessedFrom) {
     if (descriptor.delegatesToLambdaImplMethod()) {
       return createLambdaImplMethodTarget(accessedFrom);
     }
@@ -360,17 +359,20 @@
     }
   }
 
-  private Target createLambdaImplMethodTarget(DexType accessedFrom) {
+  private Target createLambdaImplMethodTarget(ProgramMethod accessedFrom) {
     DexMethodHandle implHandle = descriptor.implHandle;
     assert implHandle != null;
     DexMethod implMethod = implHandle.asMethod();
 
     // Lambda$ method. We must always find it.
-    assert implMethod.holder == accessedFrom;
-    assert descriptor.verifyTargetFoundInClass(accessedFrom);
+    assert implMethod.holder == accessedFrom.getHolderType();
+    assert descriptor.verifyTargetFoundInClass(accessedFrom.getHolderType());
     if (implHandle.type.isInvokeStatic()) {
       SingleResolutionResult resolution =
-          rewriter.getAppInfo().resolveMethod(implMethod.holder, implMethod).asSingleResolution();
+          appView
+              .appInfoForDesugaring()
+              .resolveMethod(implMethod, implHandle.isInterface)
+              .asSingleResolution();
       assert resolution.getResolvedMethod().isStatic();
       assert resolution.getResolvedHolder().isProgramClass();
       return new StaticLambdaImplTarget(
@@ -383,28 +385,28 @@
     // If the lambda$ method is an instance-private method on an interface we convert it into a
     // public static method as it will be placed on the companion class.
     if (implHandle.type.isInvokeDirect()
-        && rewriter.getAppView().definitionFor(implMethod.holder).isInterface()) {
+        && appView.definitionFor(implMethod.holder).isInterface()) {
       DexProto implProto = implMethod.proto;
       DexType[] implParams = implProto.parameters.values;
       DexType[] newParams = new DexType[implParams.length + 1];
       newParams[0] = implMethod.holder;
       System.arraycopy(implParams, 0, newParams, 1, implParams.length);
 
-      DexProto newProto = rewriter.getFactory().createProto(implProto.returnType, newParams);
+      DexProto newProto = appView.dexItemFactory().createProto(implProto.returnType, newParams);
       return new InterfaceLambdaImplTarget(
-          rewriter.getFactory().createMethod(implMethod.holder, newProto, implMethod.name));
+          appView.dexItemFactory().createMethod(implMethod.holder, newProto, implMethod.name));
     } else {
       // Otherwise we need to ensure the method can be reached publicly by virtual dispatch.
       // To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual
       // we add the method-holder name as suffix to the lambda-method name.
       return new InstanceLambdaImplTarget(
-          rewriter
-              .getFactory()
+          appView
+              .dexItemFactory()
               .createMethod(
                   implMethod.holder,
                   implMethod.proto,
-                  rewriter
-                      .getFactory()
+                  appView
+                      .dexItemFactory()
                       .createString(
                           implMethod.name.toString() + "$" + implMethod.holder.getName())));
     }
@@ -412,7 +414,7 @@
 
   // Create targets for instance method referenced directly without
   // lambda$ methods. It may require creation of accessors in some cases.
-  private Target createInstanceMethodTarget(DexType accessedFrom) {
+  private Target createInstanceMethodTarget(ProgramMethod accessedFrom) {
     assert descriptor.implHandle.type.isInvokeInstance() ||
         descriptor.implHandle.type.isInvokeDirect();
 
@@ -433,18 +435,19 @@
     accessorParams[0] = descriptor.getImplReceiverType();
     System.arraycopy(implParams, 0, accessorParams, 1, implParams.length);
     DexProto accessorProto =
-        rewriter.getFactory().createProto(implProto.returnType, accessorParams);
+        appView.dexItemFactory().createProto(implProto.returnType, accessorParams);
     DexMethod accessorMethod =
-        rewriter
-            .getFactory()
-            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+        appView
+            .dexItemFactory()
+            .createMethod(
+                accessedFrom.getHolderType(), accessorProto, generateUniqueLambdaMethodName());
 
     return new ClassMethodWithAccessorTarget(accessorMethod);
   }
 
   // Create targets for static method referenced directly without
   // lambda$ methods. It may require creation of accessors in some cases.
-  private Target createStaticMethodTarget(DexType accessedFrom) {
+  private Target createStaticMethodTarget(ProgramMethod accessedFrom) {
     assert descriptor.implHandle.type.isInvokeStatic();
 
     if (!descriptor.needsAccessor(accessedFrom)) {
@@ -455,10 +458,10 @@
     // for accessing the original static impl-method. The accessor method will be
     // static, package private with exactly same signature and the original method.
     DexMethod accessorMethod =
-        rewriter
-            .getFactory()
+        appView
+            .dexItemFactory()
             .createMethod(
-                accessedFrom,
+                accessedFrom.getHolderType(),
                 descriptor.implHandle.asMethod().proto,
                 generateUniqueLambdaMethodName());
     return new ClassMethodWithAccessorTarget(accessorMethod);
@@ -466,7 +469,7 @@
 
   // Create targets for constructor referenced directly without lambda$ methods.
   // It may require creation of accessors in some cases.
-  private Target createConstructorTarget(DexType accessedFrom) {
+  private Target createConstructorTarget(ProgramMethod accessedFrom) {
     DexMethodHandle implHandle = descriptor.implHandle;
     assert implHandle != null;
     assert implHandle.type.isInvokeConstructor();
@@ -482,24 +485,25 @@
     DexMethod implMethod = implHandle.asMethod();
     DexType returnType = implMethod.holder;
     DexProto accessorProto =
-        rewriter.getFactory().createProto(returnType, implMethod.proto.parameters.values);
+        appView.dexItemFactory().createProto(returnType, implMethod.proto.parameters.values);
     DexMethod accessorMethod =
-        rewriter
-            .getFactory()
-            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+        appView
+            .dexItemFactory()
+            .createMethod(
+                accessedFrom.getHolderType(), accessorProto, generateUniqueLambdaMethodName());
     return new ClassMethodWithAccessorTarget(accessorMethod);
   }
 
   // Create targets for interface methods.
-  private Target createInterfaceMethodTarget(DexType accessedFrom) {
+  private Target createInterfaceMethodTarget(ProgramMethod accessedFrom) {
     assert descriptor.implHandle.type.isInvokeInterface();
     assert !descriptor.needsAccessor(accessedFrom);
     return new NoAccessorMethodTarget(Invoke.Type.INTERFACE);
   }
 
   private DexString generateUniqueLambdaMethodName() {
-    return rewriter
-        .getFactory()
+    return appView
+        .dexItemFactory()
         .createString(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
   }
 
@@ -533,16 +537,8 @@
       return accessibilityBridge;
     }
 
-    DexClass definitionFor(DexType type) {
-      return rewriter.getAppInfo().app().definitionFor(type);
-    }
-
-    DexProgramClass programDefinitionFor(DexType type) {
-      return rewriter.getAppInfo().app().programDefinitionFor(type);
-    }
-
     boolean holderIsInterface() {
-      InternalOptions options = rewriter.getAppView().options();
+      InternalOptions options = appView.options();
       if (!options.isGeneratingClassFiles()) {
         // When generating dex the value of this flag on invokes does not matter (unused).
         // We cannot know if definitionFor(implMethod.holder) is null or not in that case,
@@ -554,7 +550,7 @@
       // implMethodHolder != null may fail, hence the assertion.
       assert options.cfToCfDesugar;
       DexMethod implMethod = descriptor.implHandle.asMethod();
-      DexClass implMethodHolder = definitionFor(implMethod.holder);
+      DexClass implMethodHolder = appView.definitionFor(implMethod.holder);
       if (implMethodHolder == null) {
         assert options
             .desugaredLibraryConfiguration
@@ -613,7 +609,7 @@
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
-      DexProgramClass implMethodHolder = definitionFor(implMethod.holder).asProgramClass();
+      DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
 
       DexEncodedMethod replacement =
           implMethodHolder
@@ -642,7 +638,7 @@
                     rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method);
 
                     DexEncodedMethod.setDebugInfoWithFakeThisParameter(
-                        newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
+                        newMethod.getCode(), callTarget.getArity(), appView);
                     return newMethod;
                   });
 
@@ -663,11 +659,11 @@
     @Override
     ProgramMethod ensureAccessibility(boolean allowMethodModification) {
       // When compiling with whole program optimization, check that we are not inplace modifying.
-      assert !(rewriter.getAppView().enableWholeProgramOptimizations() && allowMethodModification);
+      assert !(appView.enableWholeProgramOptimizations() && allowMethodModification);
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
-      DexProgramClass implMethodHolder = definitionFor(implMethod.holder).asProgramClass();
+      DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
       return allowMethodModification
           ? modifyLambdaImplementationMethod(implMethod, implMethodHolder)
           : createSyntheticAccessor(implMethod, implMethodHolder);
@@ -744,7 +740,7 @@
     @Override
     ProgramMethod ensureAccessibility(boolean allowMethodModification) {
       // Create a static accessor with proper accessibility.
-      DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
+      DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
       assert accessorClass != null;
 
       // Always make the method public to provide access when r8 minification is allowed to move
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index bfc9362..deb4579 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -11,13 +11,13 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
@@ -67,7 +67,7 @@
 
   private LambdaDescriptor(
       AppInfoWithClassHierarchy appInfo,
-      DexType invocationContext,
+      ProgramMethod context,
       DexCallSite callSite,
       DexString name,
       DexProto erasedProto,
@@ -92,8 +92,7 @@
     this.captures = captures;
 
     this.interfaces.add(mainInterface);
-    DexEncodedMethod targetMethod =
-        invocationContext == null ? null : lookupTargetMethod(appInfo, invocationContext);
+    DexEncodedMethod targetMethod = context == null ? null : lookupTargetMethod(appInfo, context);
     if (targetMethod != null) {
       targetAccessFlags = targetMethod.accessFlags.copy();
       targetHolder = targetMethod.holder();
@@ -113,17 +112,19 @@
   }
 
   private DexEncodedMethod lookupTargetMethod(
-      AppInfoWithClassHierarchy appInfo, DexType invocationContext) {
-    assert invocationContext != null;
+      AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
+    assert context != null;
     // Find the lambda's impl-method target.
     DexMethod method = implHandle.asMethod();
     switch (implHandle.type) {
       case INVOKE_DIRECT:
       case INVOKE_INSTANCE: {
           DexEncodedMethod target =
-              appInfo.resolveMethod(getImplReceiverType(), method).getSingleTarget();
+              appInfo
+                  .resolveMethodOn(getImplReceiverType(), method, implHandle.isInterface)
+                  .getSingleTarget();
         if (target == null) {
-            target = appInfo.lookupDirectTarget(method, invocationContext);
+            target = appInfo.lookupDirectTarget(method, context);
         }
         assert target == null
             || (implHandle.type.isInvokeInstance() && isInstanceMethod(target))
@@ -133,20 +134,20 @@
       }
 
       case INVOKE_STATIC: {
-          DexEncodedMethod target = appInfo.lookupStaticTarget(method, invocationContext);
+          DexEncodedMethod target = appInfo.lookupStaticTarget(method, context);
         assert target == null || target.accessFlags.isStatic();
         return target;
       }
 
       case INVOKE_CONSTRUCTOR: {
-          DexEncodedMethod target = appInfo.lookupDirectTarget(method, invocationContext);
+          DexEncodedMethod target = appInfo.lookupDirectTarget(method, context);
         assert target == null || target.accessFlags.isConstructor();
         return target;
       }
 
       case INVOKE_INTERFACE: {
           DexEncodedMethod target =
-              appInfo.resolveMethod(getImplReceiverType(), method).getSingleTarget();
+              appInfo.resolveMethodOnInterface(getImplReceiverType(), method).getSingleTarget();
         assert target == null || isInstanceMethod(target);
         return target;
       }
@@ -187,7 +188,7 @@
   }
 
   /** Checks if call site needs a accessor when referenced from `accessedFrom`. */
-  boolean needsAccessor(DexType accessedFrom) {
+  boolean needsAccessor(ProgramMethod accessedFrom) {
     if (delegatesToLambdaImplMethod()) {
       return false;
     }
@@ -217,8 +218,10 @@
         // because the method being called must be present in method holder,
         // and not in one from its supertypes.
         boolean accessedFromSamePackage =
-            accessedFrom.getPackageDescriptor().equals(
-                implHandle.asMethod().holder.getPackageDescriptor());
+            accessedFrom
+                .getHolderType()
+                .getPackageDescriptor()
+                .equals(implHandle.asMethod().holder.getPackageDescriptor());
         return !accessedFromSamePackage;
       }
 
@@ -238,7 +241,10 @@
     }
 
     boolean accessedFromSamePackage =
-        accessedFrom.getPackageDescriptor().equals(targetHolder.getPackageDescriptor());
+        accessedFrom
+            .getHolderType()
+            .getPackageDescriptor()
+            .equals(targetHolder.getPackageDescriptor());
     assert flags.isProtected() || accessedFromSamePackage;
     return flags.isProtected() && !accessedFromSamePackage;
   }
@@ -248,8 +254,8 @@
    * information, or null if match failed.
    */
   public static LambdaDescriptor tryInfer(
-      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, DexProgramClass invocationContext) {
-    LambdaDescriptor descriptor = infer(callSite, appInfo, invocationContext.type);
+      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
+    LambdaDescriptor descriptor = infer(callSite, appInfo, context);
     return descriptor == MATCH_FAILED ? null : descriptor;
   }
 
@@ -265,7 +271,7 @@
    * information, or MATCH_FAILED if match failed.
    */
   static LambdaDescriptor infer(
-      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, DexType invocationContext) {
+      DexCallSite callSite, AppInfoWithClassHierarchy appInfo, ProgramMethod context) {
     if (!isLambdaMetafactoryMethod(callSite, appInfo.dexItemFactory())) {
       return LambdaDescriptor.MATCH_FAILED;
     }
@@ -309,7 +315,7 @@
     LambdaDescriptor match =
         new LambdaDescriptor(
             appInfo,
-            invocationContext,
+            context,
             callSite,
             funcMethodName,
             funcErasedSignature.value,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 6b41818..aa47bdc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -4,13 +4,9 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
@@ -59,7 +55,7 @@
   static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
   private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
 
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final AppView<?> appView;
 
   final DexString instanceFieldName;
 
@@ -76,42 +72,9 @@
   // NOTE: synchronize concurrent access on `knownLambdaClasses`.
   private final Map<DexType, LambdaClass> knownLambdaClasses = new IdentityHashMap<>();
 
-  // Checks if the type starts with lambda-class prefix.
-  public static boolean hasLambdaClassPrefix(DexType clazz) {
-    return clazz.getName().startsWith(LAMBDA_CLASS_NAME_PREFIX);
-  }
-
   public LambdaRewriter(AppView<?> appView) {
-    assert appView.appInfo().hasClassHierarchy()
-        : "Lambda desugaring is not available without class hierarchy.";
-    this.appView = appView.withClassHierarchy();
-    this.instanceFieldName = getFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
-  }
-
-  public AppView<?> getAppView() {
-    return appView;
-  }
-
-  public AppInfoWithClassHierarchy getAppInfo() {
-    return appView.appInfo();
-  }
-
-  public DexItemFactory getFactory() {
-    return getAppView().dexItemFactory();
-  }
-
-  public Map<DexEncodedField, Set<DexEncodedMethod>> getWritesWithContexts(
-      DexProgramClass synthesizedLambdaClass) {
-    // Record that the static fields on each lambda class are only written inside the static
-    // initializer of the lambdas.
-    Map<DexEncodedField, Set<DexEncodedMethod>> writesWithContexts = new IdentityHashMap<>();
-      DexEncodedMethod clinit = synthesizedLambdaClass.getClassInitializer();
-      if (clinit != null) {
-        for (DexEncodedField field : synthesizedLambdaClass.staticFields()) {
-          writesWithContexts.put(field, ImmutableSet.of(clinit));
-        }
-      }
-    return writesWithContexts;
+    this.appView = appView;
+    this.instanceFieldName = appView.dexItemFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
   }
 
   private void synthesizeAccessibilityBridgesForLambdaClassesD8(
@@ -136,9 +99,9 @@
    *
    * <p>NOTE: this method can be called concurrently for several different methods.
    */
-  public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) {
+  public void desugarLambdas(IRCode code) {
     Set<Value> affectedValues = Sets.newIdentityHashSet();
-    DexType currentType = encodedMethod.holder();
+    ProgramMethod context = code.context();
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -147,8 +110,7 @@
         Instruction instruction = instructions.next();
         if (instruction.isInvokeCustom()) {
           InvokeCustom invoke = instruction.asInvokeCustom();
-          LambdaDescriptor descriptor =
-              inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.holder());
+          LambdaDescriptor descriptor = inferLambdaDescriptor(invoke.getCallSite(), context);
           if (descriptor == LambdaDescriptor.MATCH_FAILED) {
             continue;
           }
@@ -156,9 +118,9 @@
           // We have a descriptor, get the lambda class. In D8, we synthesize the lambda classes
           // during IR processing, and therefore we may need to create it now.
           LambdaClass lambdaClass =
-              getAppView().enableWholeProgramOptimizations()
-                  ? getKnownLambdaClass(descriptor, currentType)
-                  : getOrCreateLambdaClass(descriptor, currentType);
+              appView.enableWholeProgramOptimizations()
+                  ? getKnownLambdaClass(descriptor, context)
+                  : getOrCreateLambdaClass(descriptor, context);
           assert lambdaClass != null;
 
           // We rely on patch performing its work in a way which
@@ -169,7 +131,7 @@
       }
     }
     if (!affectedValues.isEmpty()) {
-      new TypeAnalysis(getAppView()).narrowing(affectedValues);
+      new TypeAnalysis(appView).narrowing(affectedValues);
     }
     assert code.isConsistentSSA();
   }
@@ -177,7 +139,7 @@
   /** Remove lambda deserialization methods. */
   public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
     for (DexProgramClass clazz : classes) {
-      clazz.removeMethod(getFactory().deserializeLambdaMethod);
+      clazz.removeMethod(appView.dexItemFactory().deserializeLambdaMethod);
     }
   }
 
@@ -189,7 +151,7 @@
         knownLambdaClasses.values(), converter, executorService);
     for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
       DexProgramClass synthesizedClass = lambdaClass.getOrCreateLambdaClass();
-      getAppInfo().addSynthesizedClass(synthesizedClass);
+      appView.appInfo().addSynthesizedClass(synthesizedClass);
       builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
     }
     optimizeSynthesizedClasses(converter, executorService);
@@ -204,17 +166,11 @@
         executorService);
   }
 
-  public Set<DexCallSite> getDesugaredCallSites() {
-    synchronized (knownCallSites) {
-      return knownCallSites.keySet();
-    }
-  }
-
   // Matches invoke-custom instruction operands to infer lambda descriptor
   // corresponding to this lambda invocation point.
   //
   // Returns the lambda descriptor or `MATCH_FAILED`.
-  private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite, DexType invocationContext) {
+  private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite, ProgramMethod context) {
     // We check the map before and after inferring lambda descriptor to minimize time
     // spent in synchronized block. As a result we may throw away calculated descriptor
     // in rare case when another thread has same call site processed concurrently,
@@ -225,17 +181,18 @@
         : putIfAbsent(
             knownCallSites,
             callSite,
-            LambdaDescriptor.infer(callSite, getAppInfo(), invocationContext));
+            LambdaDescriptor.infer(callSite, appView.appInfoForDesugaring(), context));
   }
 
   private boolean isInMainDexList(DexType type) {
-    return getAppInfo().isInMainDexList(type);
+    return appView.appInfo().isInMainDexList(type);
   }
 
   // Returns a lambda class corresponding to the lambda descriptor and context,
   // creates the class if it does not yet exist.
-  public LambdaClass getOrCreateLambdaClass(LambdaDescriptor descriptor, DexType accessedFrom) {
-    DexType lambdaClassType = LambdaClass.createLambdaClassType(this, accessedFrom, descriptor);
+  public LambdaClass getOrCreateLambdaClass(
+      LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
+    DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
     // We check the map twice to to minimize time spent in synchronized block.
     LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
     if (lambdaClass == null) {
@@ -243,50 +200,50 @@
           putIfAbsent(
               knownLambdaClasses,
               lambdaClassType,
-              new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
-      if (getAppView().options().isDesugaredLibraryCompilation()) {
+              new LambdaClass(appView, this, accessedFrom, lambdaClassType, descriptor));
+      if (appView.options().isDesugaredLibraryCompilation()) {
         DexType rewrittenType =
-            getAppView().rewritePrefix.rewrittenType(accessedFrom, getAppView());
+            appView.rewritePrefix.rewrittenType(accessedFrom.getHolderType(), appView);
         if (rewrittenType == null) {
           rewrittenType =
-              getAppView()
+              appView
                   .options()
                   .desugaredLibraryConfiguration
                   .getEmulateLibraryInterface()
-                  .get(accessedFrom);
+                  .get(accessedFrom.getHolderType());
         }
         if (rewrittenType != null) {
           addRewritingPrefix(accessedFrom, rewrittenType, lambdaClassType);
         }
       }
     }
-    lambdaClass.addSynthesizedFrom(getAppView().definitionFor(accessedFrom).asProgramClass());
-    if (isInMainDexList(accessedFrom)) {
+    lambdaClass.addSynthesizedFrom(accessedFrom.getHolder());
+    if (isInMainDexList(accessedFrom.getHolderType())) {
       lambdaClass.addToMainDexList.set(true);
     }
     return lambdaClass;
   }
 
-  private LambdaClass getKnownLambdaClass(LambdaDescriptor descriptor, DexType accessedFrom) {
-    DexType lambdaClassType = LambdaClass.createLambdaClassType(this, accessedFrom, descriptor);
+  private LambdaClass getKnownLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
+    DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
     return getKnown(knownLambdaClasses, lambdaClassType);
   }
 
-  private void addRewritingPrefix(DexType type, DexType rewritten, DexType lambdaClassType) {
+  private void addRewritingPrefix(
+      ProgramMethod context, DexType rewritten, DexType lambdaClassType) {
     String javaName = lambdaClassType.toString();
-    String typeString = type.toString();
+    String typeString = context.getHolderType().toString();
     String actualPrefix = typeString.substring(0, typeString.lastIndexOf('.'));
     String rewrittenString = rewritten.toString();
     String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
     assert javaName.startsWith(actualPrefix);
-    getAppView()
-        .rewritePrefix
-        .rewriteType(
-            lambdaClassType,
-            getFactory()
-                .createType(
-                    DescriptorUtils.javaTypeToDescriptor(
-                        actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
+    appView.rewritePrefix.rewriteType(
+        lambdaClassType,
+        appView
+            .dexItemFactory()
+            .createType(
+                DescriptorUtils.javaTypeToDescriptor(
+                    actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
   }
 
   private static <K, V> V getKnown(Map<K, V> map, K key) {
@@ -326,7 +283,7 @@
       // The out value might be empty in case it was optimized out.
       lambdaInstanceValue =
           code.createValue(
-              TypeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), getAppView()));
+              TypeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
     } else {
       affectedValues.add(lambdaInstanceValue);
     }
@@ -379,7 +336,7 @@
     BasicBlock currentBlock = newInstance.getBlock();
     BasicBlock nextBlock = instructions.split(code, blocks);
     assert !instructions.hasNext();
-    nextBlock.copyCatchHandlers(code, blocks, currentBlock, getAppView().options());
+    nextBlock.copyCatchHandlers(code, blocks, currentBlock, appView.options());
   }
 
   public Map<DexType, LambdaClass> getKnownLambdaClasses() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
index 7e50b88..5643288 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
@@ -21,7 +21,7 @@
   }
 
   final DexItemFactory dexItemFactory() {
-    return lambda.rewriter.getFactory();
+    return lambda.appView.dexItemFactory();
   }
 
   final LambdaDescriptor descriptor() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
index 7677cee..fc12713 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -40,7 +40,7 @@
   }
 
   final DexItemFactory factory() {
-    return lambda.rewriter.getFactory();
+    return lambda.appView.dexItemFactory();
   }
 
   final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 8d1124b..8912aeb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.ir.optimize;
 
 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.DexMethodHandle;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -85,7 +84,7 @@
     if (mode != Mode.COLLECT) {
       return;
     }
-    DexEncodedMethod context = code.method();
+    ProgramMethod context = code.context();
     for (Instruction instruction : code.instructions()) {
       if (!instruction.isInvokeMethod() && !instruction.isInvokeCustom()) {
         continue;
@@ -95,26 +94,28 @@
         if (invoke.isInvokeMethodWithDynamicDispatch()) {
           DexMethod invokedMethod = invoke.getInvokedMethod();
           ResolutionResult resolutionResult =
-              appView.appInfo().resolveMethod(invokedMethod.holder, invokedMethod);
+              appView.appInfo().resolveMethod(invokedMethod, invoke.isInvokeInterface());
           // For virtual and interface calls, proceed on valid results only (since it's enforced).
-          if (!resolutionResult.isVirtualTarget()) {
+          if (!resolutionResult.isSingleResolution() || !resolutionResult.isVirtualTarget()) {
             continue;
           }
           // If the resolution ended up with a single target, check if it is a library override.
           // And if so, bail out early (to avoid expensive target lookup).
-          if (resolutionResult.isSingleResolution()
-              && isLibraryMethodOrLibraryMethodOverride(resolutionResult.getSingleTarget())) {
+          ProgramMethod resolutionTarget =
+              resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod();
+          if (resolutionTarget == null
+              || isLibraryMethodOrLibraryMethodOverride(resolutionTarget)) {
             continue;
           }
         }
-        Collection<DexEncodedMethod> targets = invoke.lookupTargets(appView, context.holder());
+        ProgramMethodSet targets = invoke.lookupProgramDispatchTargets(appView, context);
         assert invoke.isInvokeMethodWithDynamicDispatch()
             // For other invocation types, the size of targets should be at most one.
             || targets == null || targets.size() <= 1;
         if (targets == null || targets.isEmpty() || hasLibraryOverrides(targets)) {
           continue;
         }
-        for (DexEncodedMethod target : targets) {
+        for (ProgramMethod target : targets) {
           recordArgumentsIfNecessary(target, invoke.inValues());
         }
       }
@@ -132,7 +133,6 @@
             appView
                 .appInfo()
                 .resolveMethod(
-                    bootstrapMethod.asMethod().holder,
                     bootstrapMethod.asMethod(),
                     bootstrapMethod.isInterface)
                 .asSingleResolution();
@@ -147,8 +147,8 @@
 
   // TODO(b/140204899): Instead of reprocessing here, pass stopping criteria to lookup?
   // If any of target method is a library method override, bail out entirely/early.
-  private boolean hasLibraryOverrides(Collection<DexEncodedMethod> targets) {
-    for (DexEncodedMethod target : targets) {
+  private boolean hasLibraryOverrides(ProgramMethodSet targets) {
+    for (ProgramMethod target : targets) {
       if (isLibraryMethodOrLibraryMethodOverride(target)) {
         return true;
       }
@@ -156,54 +156,42 @@
     return false;
   }
 
-  private boolean isLibraryMethodOrLibraryMethodOverride(DexEncodedMethod target) {
-    // Not a program method.
-    if (!target.isProgramMethod(appView)) {
-      return true;
-    }
+  private boolean isLibraryMethodOrLibraryMethodOverride(ProgramMethod target) {
     // If the method overrides a library method, it is unsure how the method would be invoked by
     // that library.
-    if (target.isLibraryMethodOverride().isTrue()) {
-      return true;
-    }
-    return false;
+    return target.getDefinition().isLibraryMethodOverride().isTrue();
   }
 
   // Record arguments for the given method if necessary.
   // At the same time, if it decides to bail out, make the corresponding info immutable so that we
   // can avoid recording arguments for the same method accidentally.
-  private void recordArgumentsIfNecessary(DexEncodedMethod target, List<Value> inValues) {
-    assert !target.isObsolete();
-    if (appView.appInfo().neverReprocess.contains(target.method)) {
+  private void recordArgumentsIfNecessary(ProgramMethod target, List<Value> inValues) {
+    assert !target.getDefinition().isObsolete();
+    if (appView.appInfo().neverReprocess.contains(target.getReference())) {
       return;
     }
-    if (target.getCallSiteOptimizationInfo().isTop()) {
+    if (target.getDefinition().getCallSiteOptimizationInfo().isTop()) {
       return;
     }
-    target.joinCallSiteOptimizationInfo(
-        computeCallSiteOptimizationInfoFromArguments(target, inValues), appView);
+    target
+        .getDefinition()
+        .joinCallSiteOptimizationInfo(
+            computeCallSiteOptimizationInfoFromArguments(target, inValues), appView);
   }
 
   private CallSiteOptimizationInfo computeCallSiteOptimizationInfoFromArguments(
-      DexEncodedMethod target, List<Value> inValues) {
+      ProgramMethod target, List<Value> inValues) {
     // No method body or no argument at all.
-    if (target.shouldNotHaveCode() || inValues.size() == 0) {
+    if (target.getDefinition().shouldNotHaveCode() || inValues.size() == 0) {
       return CallSiteOptimizationInfo.TOP;
     }
     // If pinned, that method could be invoked via reflection.
-    if (appView.appInfo().isPinned(target.method)) {
-      return CallSiteOptimizationInfo.TOP;
-    }
-    // Not a program method.
-    if (!target.isProgramMethod(appView)) {
-      // But, should not be reachable, since we already bail out.
-      assert false
-          : "Trying to compute call site optimization info for " + target.toSourceString();
+    if (appView.appInfo().isPinned(target.getReference())) {
       return CallSiteOptimizationInfo.TOP;
     }
     // If the method overrides a library method, it is unsure how the method would be invoked by
     // that library.
-    if (target.isLibraryMethodOverride().isTrue()) {
+    if (target.getDefinition().isLibraryMethodOverride().isTrue()) {
       // But, should not be reachable, since we already bail out.
       assert false
           : "Trying to compute call site optimization info for " + target.toSourceString();
@@ -212,8 +200,8 @@
     // If the program already has illegal accesses, method resolution results will reflect that too.
     // We should avoid recording arguments in that case. E.g., b/139823850: static methods can be a
     // result of virtual call targets, if that's the only method that matches name and signature.
-    int argumentOffset = target.isStatic() ? 0 : 1;
-    if (inValues.size() != argumentOffset + target.method.getArity()) {
+    int argumentOffset = target.getDefinition().isStatic() ? 0 : 1;
+    if (inValues.size() != argumentOffset + target.getReference().getArity()) {
       return CallSiteOptimizationInfo.BOTTOM;
     }
     return ConcreteCallSiteOptimizationInfo.fromArguments(appView, target, inValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index ce853a2..a7a24cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
-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.DexMethod;
@@ -29,6 +28,8 @@
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.ValueMayDependOnEnvironmentAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
@@ -53,7 +54,6 @@
 import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -123,18 +123,17 @@
     this.dexItemFactory = appView.dexItemFactory();
   }
 
-  public ClassInitializerDefaultsResult optimize(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
-    if (appView.options().debug || method.getOptimizationInfo().isReachabilitySensitive()) {
+  public ClassInitializerDefaultsResult optimize(IRCode code, OptimizationFeedback feedback) {
+    if (appView.options().debug) {
       return ClassInitializerDefaultsResult.empty();
     }
 
-    if (!method.isClassInitializer()) {
+    ProgramMethod context = code.context();
+    if (context.getDefinition().getOptimizationInfo().isReachabilitySensitive()) {
       return ClassInitializerDefaultsResult.empty();
     }
 
-    DexClass clazz = appView.definitionFor(method.holder());
-    if (clazz == null) {
+    if (!context.getDefinition().isClassInitializer()) {
       return ClassInitializerDefaultsResult.empty();
     }
 
@@ -142,8 +141,9 @@
     // a static put on a field on this class with a value that can be hoisted to
     // the field initial value.
     Set<StaticPut> unnecessaryStaticPuts = Sets.newIdentityHashSet();
-    Collection<StaticPut> finalFieldPuts =
-        findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(code, clazz, unnecessaryStaticPuts);
+    Map<DexEncodedField, StaticPut> finalFieldPuts =
+        findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(
+            code, context, unnecessaryStaticPuts);
 
     // Return eagerly if there is nothing to optimize.
     if (finalFieldPuts.isEmpty()) {
@@ -154,43 +154,43 @@
     Map<DexEncodedField, DexValue> fieldsWithStaticValues = new IdentityHashMap<>();
 
     // Set initial values for static fields from the definitive static put instructions collected.
-    for (StaticPut put : finalFieldPuts) {
-      DexEncodedField field = appView.appInfo().resolveField(put.getField());
-      DexType fieldType = field.field.type;
-      Value value = put.value();
-      if (unnecessaryStaticPuts.contains(put)) {
-        if (fieldType == dexItemFactory.stringType) {
-          fieldsWithStaticValues.put(field, getDexStringValue(value, method.holder()));
-        } else if (fieldType.isClassType() || fieldType.isArrayType()) {
-          if (value.isZero()) {
-            fieldsWithStaticValues.put(field, DexValueNull.NULL);
-          } else {
-            throw new Unreachable("Unexpected default value for field type " + fieldType + ".");
+    finalFieldPuts.forEach(
+        (field, put) -> {
+          DexType fieldType = field.field.type;
+          Value value = put.value();
+          if (unnecessaryStaticPuts.contains(put)) {
+            if (fieldType == dexItemFactory.stringType) {
+              fieldsWithStaticValues.put(field, getDexStringValue(value, context.getHolderType()));
+            } else if (fieldType.isClassType() || fieldType.isArrayType()) {
+              if (value.isZero()) {
+                fieldsWithStaticValues.put(field, DexValueNull.NULL);
+              } else {
+                throw new Unreachable("Unexpected default value for field type " + fieldType + ".");
+              }
+            } else {
+              ConstNumber cnst = value.getConstInstruction().asConstNumber();
+              if (fieldType == dexItemFactory.booleanType) {
+                fieldsWithStaticValues.put(field, DexValueBoolean.create(cnst.getBooleanValue()));
+              } else if (fieldType == dexItemFactory.byteType) {
+                fieldsWithStaticValues.put(field, DexValueByte.create((byte) cnst.getIntValue()));
+              } else if (fieldType == dexItemFactory.shortType) {
+                fieldsWithStaticValues.put(field, DexValueShort.create((short) cnst.getIntValue()));
+              } else if (fieldType == dexItemFactory.intType) {
+                fieldsWithStaticValues.put(field, DexValueInt.create(cnst.getIntValue()));
+              } else if (fieldType == dexItemFactory.longType) {
+                fieldsWithStaticValues.put(field, DexValueLong.create(cnst.getLongValue()));
+              } else if (fieldType == dexItemFactory.floatType) {
+                fieldsWithStaticValues.put(field, DexValueFloat.create(cnst.getFloatValue()));
+              } else if (fieldType == dexItemFactory.doubleType) {
+                fieldsWithStaticValues.put(field, DexValueDouble.create(cnst.getDoubleValue()));
+              } else if (fieldType == dexItemFactory.charType) {
+                fieldsWithStaticValues.put(field, DexValueChar.create((char) cnst.getIntValue()));
+              } else {
+                throw new Unreachable("Unexpected field type " + fieldType + ".");
+              }
+            }
           }
-        } else {
-          ConstNumber cnst = value.getConstInstruction().asConstNumber();
-          if (fieldType == dexItemFactory.booleanType) {
-            fieldsWithStaticValues.put(field, DexValueBoolean.create(cnst.getBooleanValue()));
-          } else if (fieldType == dexItemFactory.byteType) {
-            fieldsWithStaticValues.put(field, DexValueByte.create((byte) cnst.getIntValue()));
-          } else if (fieldType == dexItemFactory.shortType) {
-            fieldsWithStaticValues.put(field, DexValueShort.create((short) cnst.getIntValue()));
-          } else if (fieldType == dexItemFactory.intType) {
-            fieldsWithStaticValues.put(field, DexValueInt.create(cnst.getIntValue()));
-          } else if (fieldType == dexItemFactory.longType) {
-            fieldsWithStaticValues.put(field, DexValueLong.create(cnst.getLongValue()));
-          } else if (fieldType == dexItemFactory.floatType) {
-            fieldsWithStaticValues.put(field, DexValueFloat.create(cnst.getFloatValue()));
-          } else if (fieldType == dexItemFactory.doubleType) {
-            fieldsWithStaticValues.put(field, DexValueDouble.create(cnst.getDoubleValue()));
-          } else if (fieldType == dexItemFactory.charType) {
-            fieldsWithStaticValues.put(field, DexValueChar.create((char) cnst.getIntValue()));
-          } else {
-            throw new Unreachable("Unexpected field type " + fieldType + ".");
-          }
-        }
-      }
-    }
+        });
 
     if (!unnecessaryStaticPuts.isEmpty()) {
       // Remove the static put instructions now replaced by static field initial values.
@@ -239,10 +239,11 @@
 
         // First collect all the candidate fields that are *potentially* no longer being written to.
         Set<DexField> candidates =
-            finalFieldPuts.stream()
+            finalFieldPuts.values().stream()
                 .filter(unnecessaryStaticPuts::contains)
                 .map(FieldInstruction::getField)
                 .map(appInfoWithLiveness::resolveField)
+                .map(FieldResolutionResult::getResolvedField)
                 .filter(appInfoWithLiveness::isStaticFieldWrittenOnlyInEnclosingStaticInitializer)
                 .map(field -> field.field)
                 .collect(Collectors.toSet());
@@ -252,7 +253,8 @@
           if (instruction.isStaticPut()) {
             StaticPut staticPutInstruction = instruction.asStaticPut();
             DexField field = staticPutInstruction.getField();
-            DexEncodedField encodedField = appInfoWithLiveness.resolveField(field);
+            DexEncodedField encodedField =
+                appInfoWithLiveness.resolveField(field).getResolvedField();
             if (encodedField != null) {
               candidates.remove(encodedField.field);
             }
@@ -357,13 +359,13 @@
     return null;
   }
 
-  private Collection<StaticPut> findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(
-      IRCode code, DexClass clazz, Set<StaticPut> unnecessaryStaticPuts) {
+  private Map<DexEncodedField, StaticPut> findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(
+      IRCode code, ProgramMethod context, Set<StaticPut> unnecessaryStaticPuts) {
     ValueMayDependOnEnvironmentAnalysis environmentAnalysis =
         new ValueMayDependOnEnvironmentAnalysis(appView, code);
-    Map<DexField, StaticPut> finalFieldPuts = Maps.newIdentityHashMap();
+    Map<DexEncodedField, StaticPut> finalFieldPuts = Maps.newIdentityHashMap();
     Map<DexField, Set<StaticPut>> isWrittenBefore = Maps.newIdentityHashMap();
-    Set<DexField> isReadBefore = Sets.newIdentityHashSet();
+    Set<DexEncodedField> isReadBefore = Sets.newIdentityHashSet();
     final int color = code.reserveMarkingColor();
     try {
       BasicBlock block = code.entryBlock();
@@ -374,29 +376,30 @@
             // Array stores do not impact our ability to move constants into the class definition,
             // as long as the instructions do not throw.
             ArrayPut arrayPut = instruction.asArrayPut();
-            if (arrayPut.instructionInstanceCanThrow(appView, clazz.type).isThrowing()) {
+            if (arrayPut.instructionInstanceCanThrow(appView, context).isThrowing()) {
               return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
             }
           } else if (instruction.isStaticGet()) {
             StaticGet get = instruction.asStaticGet();
-            DexEncodedField field = appView.appInfo().resolveField(get.getField());
-            if (field != null && field.holder() == clazz.type) {
-              isReadBefore.add(field.field);
-            } else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
+            DexEncodedField field = context.getHolder().lookupField(get.getField());
+            if (field != null) {
+              isReadBefore.add(field);
+            } else {
               // Reading another field is only OK if the read does not have side-effects.
               return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
             }
           } else if (instruction.isStaticPut()) {
             StaticPut put = instruction.asStaticPut();
-            if (put.getField().holder != clazz.type) {
+            if (put.getField().holder != context.getHolderType()) {
               // Can cause clinit on another class which can read uninitialized static fields
               // of this class.
               return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
             }
-            DexField field = put.getField();
+            DexField fieldReference = put.getField();
+            DexEncodedField field = context.getHolder().lookupField(fieldReference);
             Value value = put.value();
             TypeElement valueType = value.getType();
-            if (clazz.definesStaticField(field)) {
+            if (field != null) {
               if (isReadBefore.contains(field)) {
                 // Promoting this put to a class constant would cause a previous static-get
                 // instruction to read a different value.
@@ -406,37 +409,37 @@
                 continue;
               }
               if (value.isConstant()) {
-                if (field.type.isReferenceType() && value.isZero()) {
+                if (fieldReference.type.isReferenceType() && value.isZero()) {
                   finalFieldPuts.put(field, put);
                   unnecessaryStaticPuts.add(put);
                   // If this field has been written before, those static-put's up to this point are
                   // redundant. We should remove them all together; otherwise, remaining static-put
                   // that is not constant can change the program semantics. See b/138912149.
-                  if (isWrittenBefore.containsKey(field)) {
-                    unnecessaryStaticPuts.addAll(isWrittenBefore.get(field));
-                    isWrittenBefore.remove(field);
+                  if (isWrittenBefore.containsKey(fieldReference)) {
+                    unnecessaryStaticPuts.addAll(isWrittenBefore.get(fieldReference));
+                    isWrittenBefore.remove(fieldReference);
                   }
                   continue;
-                } else if (field.type.isPrimitiveType()
-                    || field.type == dexItemFactory.stringType) {
+                } else if (fieldReference.type.isPrimitiveType()
+                    || fieldReference.type == dexItemFactory.stringType) {
                   finalFieldPuts.put(field, put);
                   unnecessaryStaticPuts.add(put);
-                  if (isWrittenBefore.containsKey(field)) {
-                    unnecessaryStaticPuts.addAll(isWrittenBefore.get(field));
-                    isWrittenBefore.remove(field);
+                  if (isWrittenBefore.containsKey(fieldReference)) {
+                    unnecessaryStaticPuts.addAll(isWrittenBefore.get(fieldReference));
+                    isWrittenBefore.remove(fieldReference);
                   }
                   continue;
                 }
                 // Still constant, but not able to represent it as static encoded values, e.g.,
                 // const-class, const-method-handle, etc. This static-put can be redundant if the
                 // field is rewritten with another constant. Will fall through and track static-put.
-              } else if (isClassNameConstantOf(clazz, put)) {
+              } else if (isClassNameConstantOf(context.getHolder(), put)) {
                 // Collect put of class name constant as a potential default value.
                 finalFieldPuts.put(field, put);
                 unnecessaryStaticPuts.add(put);
-                if (isWrittenBefore.containsKey(field)) {
-                  unnecessaryStaticPuts.addAll(isWrittenBefore.get(field));
-                  isWrittenBefore.remove(field);
+                if (isWrittenBefore.containsKey(fieldReference)) {
+                  unnecessaryStaticPuts.addAll(isWrittenBefore.get(fieldReference));
+                  isWrittenBefore.remove(fieldReference);
                 }
                 continue;
               } else if (valueType.isReferenceType() && valueType.isDefinitelyNotNull()) {
@@ -448,19 +451,19 @@
               // However, if static-put is still remaining in `isWrittenBefore`, that indicates
               // the previous candidate as final field put is no longer valid.
               isWrittenBefore
-                  .computeIfAbsent(field, ignore -> Sets.newIdentityHashSet())
+                  .computeIfAbsent(fieldReference, ignore -> Sets.newIdentityHashSet())
                   .add(put);
             } else {
               // Writing another field is not OK.
               return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
             }
-          } else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
+          } else if (instruction.instructionMayHaveSideEffects(appView, context)) {
             // Some other instruction that has side-effects. Stop here.
             return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
           } else {
             // TODO(b/120138731): This check should be removed when the Class.get*Name()
             // optimizations become enabled.
-            if (isClassNameConstantOf(clazz, instruction)) {
+            if (isClassNameConstantOf(context.getHolder(), instruction)) {
               // OK, this does not read one of the fields in the enclosing class.
               continue;
             }
@@ -489,8 +492,8 @@
     return validateFinalFieldPuts(finalFieldPuts, isWrittenBefore);
   }
 
-  private Collection<StaticPut> validateFinalFieldPuts(
-      Map<DexField, StaticPut> finalFieldPuts,
+  private Map<DexEncodedField, StaticPut> validateFinalFieldPuts(
+      Map<DexEncodedField, StaticPut> finalFieldPuts,
       Map<DexField, Set<StaticPut>> isWrittenBefore) {
     // If a field is rewritten again with other values that we can't represent as static encoded
     // values, that would be recorded at `isWrittenBefore`, which is used to collect and remove
@@ -512,7 +515,7 @@
     // values, leaving it can cause incorrect optimizations. Thus, we invalidate candidates of
     // final field puts at all.
     isWrittenBefore.keySet().forEach(finalFieldPuts::remove);
-    return finalFieldPuts.values();
+    return finalFieldPuts;
   }
 
   // Check if the static put is a constant derived from the class holding the method.
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 3dbe05d..29a0c32 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
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -312,7 +313,7 @@
 
           boolean canDetachValueIsNullTarget = true;
           for (Instruction i : valueIsNullTarget.instructionsBefore(throwInstruction)) {
-            if (!i.isBlockLocalInstructionWithoutSideEffects(appView, code.method().holder())) {
+            if (!i.isBlockLocalInstructionWithoutSideEffects(appView, code.context())) {
               canDetachValueIsNullTarget = false;
               break;
             }
@@ -1133,7 +1134,7 @@
     BasicBlock defaultTarget = theSwitch.fallthroughBlock();
     SwitchCaseEliminator eliminator = null;
     BasicBlockBehavioralSubsumption behavioralSubsumption =
-        new BasicBlockBehavioralSubsumption(appView, code.method().holder());
+        new BasicBlockBehavioralSubsumption(appView, code.context());
 
     // Compute the set of switch cases that can be removed.
     int alwaysHitCase = -1;
@@ -1258,7 +1259,7 @@
         }
 
         // Check if the invoked method is known to return one of its arguments.
-        DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method().holder());
+        DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.context());
         if (target != null && target.getOptimizationInfo().returnsArgument()) {
           int argumentIndex = target.getOptimizationInfo().getReturnedArgument();
           // Replace the out value of the invoke with the argument and ignore the out value.
@@ -1380,7 +1381,7 @@
     // If the cast type is not accessible in the current context, we should not remove the cast
     // in order to preserve IllegalAccessError. Note that JVM and ART behave differently: see
     // {@link com.android.tools.r8.ir.optimize.checkcast.IllegalAccessErrorTest}.
-    if (!isTypeVisibleFromContext(appView, code.method().holder(), castType)) {
+    if (!isTypeVisibleFromContext(appView, code.context(), castType)) {
       return RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS;
     }
 
@@ -1437,7 +1438,7 @@
       InstanceOf instanceOf, InstructionListIterator it, IRCode code) {
     // If the instance-of type is not accessible in the current context, we should not remove the
     // instance-of instruction in order to preserve IllegalAccessError.
-    if (!isTypeVisibleFromContext(appView, code.method().holder(), instanceOf.type())) {
+    if (!isTypeVisibleFromContext(appView, code.context(), instanceOf.type())) {
       return false;
     }
 
@@ -2505,7 +2506,7 @@
               }
             }
           } else {
-            DexType context = code.method().holder();
+            ProgramMethod context = code.context();
             AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
             if (abstractValue.isSingleConstClassValue()) {
               AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
@@ -2822,7 +2823,7 @@
 
         InvokeMethod invoke = insn.asInvokeMethod();
         DexEncodedMethod singleTarget =
-            invoke.lookupSingleTarget(appView.withLiveness(), code.method().holder());
+            invoke.lookupSingleTarget(appView.withLiveness(), code.context());
         if (singleTarget == null || !singleTarget.getOptimizationInfo().neverReturnsNormally()) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 691de34..36fba35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -11,8 +11,7 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -87,8 +86,7 @@
   }
 
   public void canonicalize(AppView<?> appView, IRCode code) {
-    DexEncodedMethod method = code.method();
-    DexType context = method.holder();
+    ProgramMethod context = code.context();
     Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Value>> valuesDefinedByConstant =
         new Object2ObjectLinkedOpenCustomHashMap<>(
             new Strategy<Instruction>() {
@@ -149,7 +147,8 @@
           continue;
         }
         SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
-        if (method.isClassInitializer() && method.holder() == singleFieldValue.getField().holder) {
+        if (context.getDefinition().isClassInitializer()
+            && context.getHolderType() == singleFieldValue.getField().holder) {
           // Avoid that canonicalization inserts a read before the unique write in the class
           // initializer, as that would change the program behavior.
           continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index e0066a7..4b8ee00 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -328,11 +327,7 @@
       ClassInitializationAnalysis classInitializationAnalysis,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     InlineAction action = new InlineAction(singleTarget, invoke, reason);
-    if (isTargetClassInitialized(
-        invoke,
-        method.getDefinition(),
-        singleTarget.getDefinition(),
-        classInitializationAnalysis)) {
+    if (isTargetClassInitialized(invoke, method, singleTarget, classInitializationAnalysis)) {
       return action;
     }
     if (appView.canUseInitClass()
@@ -346,8 +341,8 @@
 
   private boolean isTargetClassInitialized(
       InvokeStatic invoke,
-      DexEncodedMethod method,
-      DexEncodedMethod target,
+      ProgramMethod context,
+      ProgramMethod target,
       ClassInitializationAnalysis classInitializationAnalysis) {
     // Only proceed with inlining a static invoke if:
     // - the holder for the target is a subtype of the holder for the method,
@@ -356,28 +351,24 @@
     // - the current method has already triggered the holder for the target method to be
     //   initialized, or
     // - there is no non-trivial class initializer.
-    DexType targetHolder = target.holder();
-    if (appView.appInfo().isSubtype(method.holder(), targetHolder)) {
+    if (appView.appInfo().isSubtype(context.getHolderType(), target.getHolderType())) {
       return true;
     }
-    DexClass clazz = appView.definitionFor(targetHolder);
-    assert clazz != null;
-    if (target.getOptimizationInfo().triggersClassInitBeforeAnySideEffect()) {
+    if (target.getDefinition().getOptimizationInfo().triggersClassInitBeforeAnySideEffect()) {
       return true;
     }
-    if (!method.isStatic()) {
+    if (!context.getDefinition().isStatic()) {
       boolean targetIsGuaranteedToBeInitialized =
           appView.withInitializedClassesInInstanceMethods(
               analysis ->
-                  analysis.isClassDefinitelyLoadedInInstanceMethodsOn(
-                      target.holder(), method.holder()),
+                  analysis.isClassDefinitelyLoadedInInstanceMethod(target.getHolder(), context),
               false);
       if (targetIsGuaranteedToBeInitialized) {
         return true;
       }
     }
     if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction(
-        target.holder(), invoke)) {
+        target.getHolderType(), invoke)) {
       return true;
     }
     // Check for class initializer side effects when loading this class, as inlining might remove
@@ -387,11 +378,11 @@
     //
     // For simplicity, we are conservative and consider all interfaces, not only the ones with
     // default methods.
-    if (!clazz.classInitializationMayHaveSideEffects(appView)) {
+    if (!target.getHolder().classInitializationMayHaveSideEffects(appView)) {
       return true;
     }
 
-    if (appView.rootSet().bypassClinitForInlining.contains(target.method)) {
+    if (appView.rootSet().bypassClinitForInlining.contains(target.getReference())) {
       return true;
     }
 
@@ -488,7 +479,7 @@
         // Final fields may not be initialized outside of a constructor in the enclosing class.
         InstancePut instancePut = instruction.asInstancePut();
         DexField field = instancePut.getField();
-        DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.holder, field);
+        DexEncodedField target = appView.appInfo().lookupInstanceTarget(field);
         if (target == null || target.accessFlags.isFinal()) {
           whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToFinalFieldAssignment(
               instancePut);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 0366798..8117d6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -7,8 +7,8 @@
 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.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -52,8 +52,9 @@
     this.appView = appView;
   }
 
-  public void devirtualizeInvokeInterface(IRCode code, DexProgramClass context) {
+  public void devirtualizeInvokeInterface(IRCode code) {
     Set<Value> affectedValues = Sets.newIdentityHashSet();
+    ProgramMethod context = code.context();
     Map<InvokeInterface, InvokeVirtual> devirtualizedCall = new IdentityHashMap<>();
     DominatorTree dominatorTree = new DominatorTree(code);
     Map<Value, Map<DexType, Value>> castedReceiverCache = new IdentityHashMap<>();
@@ -118,14 +119,14 @@
         if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) {
           if (current.isInvokeSuper()) {
             InvokeSuper invoke = current.asInvokeSuper();
-            DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.type);
+            DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
             if (singleTarget != null) {
               DexClass holder = appView.definitionForHolder(singleTarget);
               assert holder != null;
               DexMethod invokedMethod = invoke.getInvokedMethod();
               DexEncodedMethod newSingleTarget =
                   InvokeVirtual.lookupSingleTarget(
-                      appView, context.type, invokedMethod, invoke.getReceiver());
+                      appView, context, invoke.getReceiver(), invokedMethod);
               if (newSingleTarget == singleTarget) {
                 it.replaceCurrentInstruction(
                     new InvokeVirtual(invokedMethod, invoke.outValue(), invoke.arguments()));
@@ -139,7 +140,7 @@
           InvokeVirtual invoke = current.asInvokeVirtual();
           DexMethod invokedMethod = invoke.getInvokedMethod();
           DexMethod reboundTarget =
-              rebindVirtualInvokeToMostSpecific(invokedMethod, invoke.getReceiver(), context.type);
+              rebindVirtualInvokeToMostSpecific(invokedMethod, invoke.getReceiver(), context);
           if (reboundTarget != invokedMethod) {
             it.replaceCurrentInstruction(
                 new InvokeVirtual(reboundTarget, invoke.outValue(), invoke.arguments()));
@@ -151,7 +152,7 @@
           continue;
         }
         InvokeInterface invoke = current.asInvokeInterface();
-        DexEncodedMethod target = invoke.lookupSingleTarget(appView, context.type);
+        DexEncodedMethod target = invoke.lookupSingleTarget(appView, context);
         if (target == null) {
           continue;
         }
@@ -163,7 +164,7 @@
         }
         // Due to the potential downcast below, make sure the new target holder is visible.
         ConstraintWithTarget visibility =
-            ConstraintWithTarget.classIsVisible(context.type, holderType, appView);
+            ConstraintWithTarget.classIsVisible(context.getHolder(), holderType, appView);
         if (visibility == ConstraintWithTarget.NEVER) {
           continue;
         }
@@ -279,7 +280,7 @@
    * entirely. Without this rewriting, we would have to keep A.foo() because the method is targeted.
    */
   private DexMethod rebindVirtualInvokeToMostSpecific(
-      DexMethod target, Value receiver, DexType context) {
+      DexMethod target, Value receiver, ProgramMethod context) {
     if (!receiver.getType().isClassType()) {
       return target;
     }
@@ -296,7 +297,8 @@
       // Virtual invoke is already as specific as it can get.
       return target;
     }
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(receiverType, target);
+    ResolutionResult resolutionResult =
+        appView.appInfo().resolveMethodOnClass(target, receiverType);
     DexEncodedMethod newTarget =
         resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
     if (newTarget == null || newTarget.method == target) {
@@ -318,10 +320,11 @@
     return target.isNonPrivateVirtualMethod() && appView.isInterface(target.holder()).isFalse();
   }
 
-  private boolean hasAccessToInvokeTargetFromContext(DexEncodedMethod target, DexType context) {
+  private boolean hasAccessToInvokeTargetFromContext(
+      DexEncodedMethod target, ProgramMethod context) {
     assert !target.accessFlags.isPrivate();
     DexType holder = target.holder();
-    if (holder == context) {
+    if (holder == context.getHolderType()) {
       // It is always safe to invoke a method from the same enclosing class.
       return true;
     }
@@ -330,7 +333,7 @@
       // Conservatively report an illegal access.
       return false;
     }
-    if (holder.isSamePackage(context)) {
+    if (holder.isSamePackage(context.getHolderType())) {
       // The class must be accessible (note that we have already established that the method is not
       // private).
       return !clazz.accessFlags.isPrivate();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index eac97c1..cbcbd2e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -78,8 +78,7 @@
               TypeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView);
           dynamicLowerBoundType = null;
         } else {
-          DexEncodedMethod singleTarget =
-              invoke.lookupSingleTarget(appView, code.method().holder());
+          DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
           if (singleTarget == null) {
             continue;
           }
@@ -96,7 +95,8 @@
         }
       } else if (current.isStaticGet()) {
         StaticGet staticGet = current.asStaticGet();
-        DexEncodedField encodedField = appView.appInfo().resolveField(staticGet.getField());
+        DexEncodedField encodedField =
+            appView.appInfo().resolveField(staticGet.getField()).getResolvedField();
         if (encodedField == null) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 62b5ab3..b1af390 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 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.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -110,7 +110,7 @@
               }
             });
 
-    DexType context = code.method().holder();
+    ProgramMethod context = code.context();
     // Collect invocations along with arguments.
     for (BasicBlock block : code.blocks) {
       for (Instruction current : block.getInstructions()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 6db2341..bb58ce1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -147,22 +147,22 @@
   }
 
   private ConstraintWithTarget instructionAllowedForInlining(
-      Instruction instruction, InliningConstraints inliningConstraints, DexType invocationContext) {
-    ConstraintWithTarget result =
-        instruction.inliningConstraint(inliningConstraints, invocationContext);
+      Instruction instruction, InliningConstraints inliningConstraints, ProgramMethod context) {
+    ConstraintWithTarget result = instruction.inliningConstraint(inliningConstraints, context);
     if (result == ConstraintWithTarget.NEVER && instruction.isDebugInstruction()) {
       return ConstraintWithTarget.ALWAYS;
     }
     return result;
   }
 
-  public ConstraintWithTarget computeInliningConstraint(IRCode code, ProgramMethod method) {
+  public ConstraintWithTarget computeInliningConstraint(IRCode code) {
     if (containsPotentialCatchHandlerVerificationError(code)) {
       return ConstraintWithTarget.NEVER;
     }
 
+    ProgramMethod context = code.context();
     if (appView.options().canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug()
-        && returnsIntAsBoolean(code, method)) {
+        && returnsIntAsBoolean(code, context)) {
       return ConstraintWithTarget.NEVER;
     }
 
@@ -171,7 +171,7 @@
         new InliningConstraints(appView, GraphLense.getIdentityLense());
     for (Instruction instruction : code.instructions()) {
       ConstraintWithTarget state =
-          instructionAllowedForInlining(instruction, inliningConstraints, method.getHolderType());
+          instructionAllowedForInlining(instruction, inliningConstraints, context);
       if (state == ConstraintWithTarget.NEVER) {
         result = state;
         break;
@@ -198,28 +198,27 @@
     return false;
   }
 
-  boolean hasInliningAccess(ProgramMethod method, ProgramMethod target) {
-    if (!isVisibleWithFlags(
-        target.getHolderType(), method.getHolderType(), target.getDefinition().accessFlags)) {
+  boolean hasInliningAccess(ProgramMethod context, ProgramMethod target) {
+    if (!isVisibleWithFlags(target.getHolderType(), context, target.getDefinition().accessFlags)) {
       return false;
     }
     // The class needs also to be visible for us to have access.
-    return isVisibleWithFlags(
-        target.getHolderType(), method.getHolderType(), target.getHolder().accessFlags);
+    return isVisibleWithFlags(target.getHolderType(), context, target.getHolder().accessFlags);
   }
 
-  private boolean isVisibleWithFlags(DexType target, DexType context, AccessFlags<?> flags) {
+  private boolean isVisibleWithFlags(DexType target, ProgramMethod context, AccessFlags<?> flags) {
     if (flags.isPublic()) {
       return true;
     }
     if (flags.isPrivate()) {
-      return NestUtils.sameNest(target, context, appView);
+      return NestUtils.sameNest(target, context.getHolderType(), appView);
     }
     if (flags.isProtected()) {
-      return appView.appInfo().isSubtype(context, target) || target.isSamePackage(context);
+      return appView.appInfo().isSubtype(context.getHolderType(), target)
+          || target.isSamePackage(context.getHolderType());
     }
     // package-private
-    return target.isSamePackage(context);
+    return target.isSamePackage(context.getHolderType());
   }
 
   public synchronized boolean isDoubleInlineSelectedTarget(ProgramMethod method) {
@@ -367,36 +366,36 @@
     }
 
     public static ConstraintWithTarget deriveConstraint(
-        DexType contextHolder, DexType targetHolder, AccessFlags flags, AppView<?> appView) {
+        DexProgramClass context, DexType targetHolder, AccessFlags<?> flags, AppView<?> appView) {
       if (flags.isPublic()) {
         return ALWAYS;
       } else if (flags.isPrivate()) {
-        DexClass contextHolderClass = appView.definitionFor(contextHolder);
-        assert contextHolderClass != null;
-        if (contextHolderClass.isInANest()) {
-          return NestUtils.sameNest(contextHolder, targetHolder, appView)
+        if (context.isInANest()) {
+          return NestUtils.sameNest(context.getType(), targetHolder, appView)
               ? new ConstraintWithTarget(Constraint.SAMENEST, targetHolder)
               : NEVER;
         }
-        return targetHolder == contextHolder
-            ? new ConstraintWithTarget(Constraint.SAMECLASS, targetHolder) : NEVER;
+        return targetHolder == context.type
+            ? new ConstraintWithTarget(Constraint.SAMECLASS, targetHolder)
+            : NEVER;
       } else if (flags.isProtected()) {
-        if (targetHolder.isSamePackage(contextHolder)) {
+        if (targetHolder.isSamePackage(context.type)) {
           // Even though protected, this is visible via the same package from the context.
           return new ConstraintWithTarget(Constraint.PACKAGE, targetHolder);
-        } else if (appView.isSubtype(contextHolder, targetHolder).isTrue()) {
+        } else if (appView.isSubtype(context.type, targetHolder).isTrue()) {
           return new ConstraintWithTarget(Constraint.SUBCLASS, targetHolder);
         }
         return NEVER;
       } else {
         /* package-private */
-        return targetHolder.isSamePackage(contextHolder)
-            ? new ConstraintWithTarget(Constraint.PACKAGE, targetHolder) : NEVER;
+        return targetHolder.isSamePackage(context.type)
+            ? new ConstraintWithTarget(Constraint.PACKAGE, targetHolder)
+            : NEVER;
       }
     }
 
     public static ConstraintWithTarget classIsVisible(
-        DexType context, DexType clazz, AppView<?> appView) {
+        DexProgramClass context, DexType clazz, AppView<?> appView) {
       if (clazz.isArrayType()) {
         return classIsVisible(context, clazz.toArrayElementType(appView.dexItemFactory()), appView);
       }
@@ -966,8 +965,7 @@
           // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()!
           ProgramMethod singleTarget = oracle.lookupSingleTarget(invoke, context);
           if (singleTarget == null) {
-            WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(
-                invoke, appView, context.getDefinition());
+            WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(invoke, appView, context);
             continue;
           }
 
@@ -975,8 +973,7 @@
           WhyAreYouNotInliningReporter whyAreYouNotInliningReporter =
               oracle.isForcedInliningOracle()
                   ? NopWhyAreYouNotInliningReporter.getInstance()
-                  : WhyAreYouNotInliningReporter.createFor(
-                      singleTargetMethod, appView, context.getDefinition());
+                  : WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context);
           InlineAction action =
               oracle.computeInlining(
                   invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index ea77caf..890a23e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -90,16 +90,16 @@
   }
 
   public ConstraintWithTarget forDexItemBasedConstString(
-      DexReference type, DexType invocationContext) {
+      DexReference type, DexProgramClass context) {
     return ConstraintWithTarget.ALWAYS;
   }
 
-  public ConstraintWithTarget forCheckCast(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forCheckCast(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
-  public ConstraintWithTarget forConstClass(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forConstClass(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
   public ConstraintWithTarget forConstInstruction() {
@@ -126,42 +126,40 @@
     return ConstraintWithTarget.ALWAYS;
   }
 
-  public ConstraintWithTarget forInitClass(DexType clazz, DexType context) {
+  public ConstraintWithTarget forInitClass(DexType clazz, DexProgramClass context) {
     return ConstraintWithTarget.classIsVisible(context, clazz, appView);
   }
 
-  public ConstraintWithTarget forInstanceGet(DexField field, DexType invocationContext) {
+  public ConstraintWithTarget forInstanceGet(DexField field, DexProgramClass context) {
     DexField lookup = graphLense.lookupField(field);
-    return forFieldInstruction(
-        lookup, appView.appInfo().lookupInstanceTarget(lookup.holder, lookup), invocationContext);
+    return forFieldInstruction(lookup, appView.appInfo().lookupInstanceTarget(lookup), context);
   }
 
-  public ConstraintWithTarget forInstanceOf(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forInstanceOf(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
-  public ConstraintWithTarget forInstancePut(DexField field, DexType invocationContext) {
+  public ConstraintWithTarget forInstancePut(DexField field, DexProgramClass context) {
     DexField lookup = graphLense.lookupField(field);
-    return forFieldInstruction(
-        lookup, appView.appInfo().lookupInstanceTarget(lookup.holder, lookup), invocationContext);
+    return forFieldInstruction(lookup, appView.appInfo().lookupInstanceTarget(lookup), context);
   }
 
-  public ConstraintWithTarget forInvoke(DexMethod method, Type type, DexType invocationContext) {
+  public ConstraintWithTarget forInvoke(DexMethod method, Type type, DexProgramClass context) {
     switch (type) {
       case DIRECT:
-        return forInvokeDirect(method, invocationContext);
+        return forInvokeDirect(method, context);
       case INTERFACE:
-        return forInvokeInterface(method, invocationContext);
+        return forInvokeInterface(method, context);
       case STATIC:
-        return forInvokeStatic(method, invocationContext);
+        return forInvokeStatic(method, context);
       case SUPER:
-        return forInvokeSuper(method, invocationContext);
+        return forInvokeSuper(method, context);
       case VIRTUAL:
-        return forInvokeVirtual(method, invocationContext);
+        return forInvokeVirtual(method, context);
       case CUSTOM:
         return forInvokeCustom();
       case POLYMORPHIC:
-        return forInvokePolymorphic(method, invocationContext);
+        return forInvokePolymorphic(method, context);
       default:
         throw new Unreachable("Unexpected type: " + type);
     }
@@ -174,17 +172,13 @@
 
   private DexEncodedMethod lookupWhileVerticalClassMerging(
       DexMethod method,
-      DexType invocationContext,
+      DexProgramClass context,
       BiFunction<SingleResolutionResult, DexProgramClass, DexEncodedMethod> lookupFunction) {
     SingleResolutionResult singleResolutionResult =
-        appView.appInfo().resolveMethod(method.holder, method).asSingleResolution();
+        appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution();
     if (singleResolutionResult == null) {
       return null;
     }
-    DexProgramClass context = appView.definitionForProgramType(invocationContext);
-    if (context == null) {
-      return null;
-    }
     DexEncodedMethod dexEncodedMethod = lookupFunction.apply(singleResolutionResult, context);
     if (dexEncodedMethod != null) {
       return dexEncodedMethod;
@@ -203,61 +197,55 @@
     return null;
   }
 
-  public ConstraintWithTarget forInvokeDirect(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokeDirect(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLense.lookupMethod(method);
     DexEncodedMethod target =
         isVerticalClassMerging()
             ? lookupWhileVerticalClassMerging(
                 lookup,
-                invocationContext,
+                context,
                 (res, ctxt) -> res.lookupInvokeDirectTarget(ctxt, appView.appInfo()))
-            : appView.appInfo().lookupDirectTarget(lookup, invocationContext);
-    return forSingleTargetInvoke(lookup, target, invocationContext);
+            : appView.appInfo().lookupDirectTarget(lookup, context);
+    return forSingleTargetInvoke(lookup, target, context);
   }
 
-  public ConstraintWithTarget forInvokeInterface(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokeInterface(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLense.lookupMethod(method);
-    return forVirtualInvoke(
-        lookup,
-        invocationContext,
-        true);
+    return forVirtualInvoke(lookup, context, true);
   }
 
-  public ConstraintWithTarget forInvokeMultiNewArray(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forInvokeMultiNewArray(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
-  public ConstraintWithTarget forInvokeNewArray(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forInvokeNewArray(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
-  public ConstraintWithTarget forInvokePolymorphic(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokePolymorphic(DexMethod method, DexProgramClass context) {
     return ConstraintWithTarget.NEVER;
   }
 
-  public ConstraintWithTarget forInvokeStatic(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokeStatic(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLense.lookupMethod(method);
     DexEncodedMethod target =
         isVerticalClassMerging()
             ? lookupWhileVerticalClassMerging(
                 lookup,
-                invocationContext,
+                context,
                 (res, ctxt) -> res.lookupInvokeStaticTarget(ctxt, appView.appInfo()))
-            : appView.appInfo().lookupStaticTarget(lookup, invocationContext);
-    return forSingleTargetInvoke(lookup, target, invocationContext);
+            : appView.appInfo().lookupStaticTarget(lookup, context);
+    return forSingleTargetInvoke(lookup, target, context);
   }
 
-  public ConstraintWithTarget forInvokeSuper(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokeSuper(DexMethod method, DexProgramClass context) {
     // The semantics of invoke super depend on the context.
-    return new ConstraintWithTarget(Constraint.SAMECLASS, invocationContext);
+    return new ConstraintWithTarget(Constraint.SAMECLASS, context.type);
   }
 
-  public ConstraintWithTarget forInvokeVirtual(DexMethod method, DexType invocationContext) {
+  public ConstraintWithTarget forInvokeVirtual(DexMethod method, DexProgramClass context) {
     DexMethod lookup = graphLense.lookupMethod(method);
-    return forVirtualInvoke(
-        lookup,
-        invocationContext,
-        false);
+    return forVirtualInvoke(lookup, context, false);
   }
 
   public ConstraintWithTarget forJumpInstruction() {
@@ -280,16 +268,16 @@
     return ConstraintWithTarget.ALWAYS;
   }
 
-  public ConstraintWithTarget forNewArrayEmpty(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forNewArrayEmpty(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
   public ConstraintWithTarget forNewArrayFilledData() {
     return ConstraintWithTarget.ALWAYS;
   }
 
-  public ConstraintWithTarget forNewInstance(DexType type, DexType invocationContext) {
-    return ConstraintWithTarget.classIsVisible(invocationContext, type, appView);
+  public ConstraintWithTarget forNewInstance(DexType type, DexProgramClass context) {
+    return ConstraintWithTarget.classIsVisible(context, type, appView);
   }
 
   public ConstraintWithTarget forAssume() {
@@ -304,16 +292,14 @@
     return ConstraintWithTarget.ALWAYS;
   }
 
-  public ConstraintWithTarget forStaticGet(DexField field, DexType invocationContext) {
+  public ConstraintWithTarget forStaticGet(DexField field, DexProgramClass context) {
     DexField lookup = graphLense.lookupField(field);
-    return forFieldInstruction(
-        lookup, appView.appInfo().lookupStaticTarget(lookup.holder, lookup), invocationContext);
+    return forFieldInstruction(lookup, appView.appInfo().lookupStaticTarget(lookup), context);
   }
 
-  public ConstraintWithTarget forStaticPut(DexField field, DexType invocationContext) {
+  public ConstraintWithTarget forStaticPut(DexField field, DexProgramClass context) {
     DexField lookup = graphLense.lookupField(field);
-    return forFieldInstruction(
-        lookup, appView.appInfo().lookupStaticTarget(lookup.holder, lookup), invocationContext);
+    return forFieldInstruction(lookup, appView.appInfo().lookupStaticTarget(lookup), context);
   }
 
   public ConstraintWithTarget forStore() {
@@ -341,17 +327,16 @@
   }
 
   private ConstraintWithTarget forFieldInstruction(
-      DexField field, DexEncodedField target, DexType invocationContext) {
+      DexField field, DexEncodedField target, DexProgramClass context) {
     // Resolve the field if possible and decide whether the instruction can inlined.
     DexType fieldHolder = graphLense.lookupType(field.holder);
     DexClass fieldClass = appView.definitionFor(fieldHolder);
     if (target != null && fieldClass != null) {
       ConstraintWithTarget fieldConstraintWithTarget =
-          ConstraintWithTarget.deriveConstraint(
-              invocationContext, fieldHolder, target.accessFlags, appView);
+          ConstraintWithTarget.deriveConstraint(context, fieldHolder, target.accessFlags, appView);
 
       // If the field has not been member-rebound, then we also need to make sure that the
-      // `invocationContext` has access to the definition of the field.
+      // `context` has access to the definition of the field.
       //
       // See, for example, InlineNonReboundFieldTest (b/128604123).
       if (field.holder != target.holder()) {
@@ -360,13 +345,13 @@
             ConstraintWithTarget.meet(
                 fieldConstraintWithTarget,
                 ConstraintWithTarget.deriveConstraint(
-                    invocationContext, actualFieldHolder, target.accessFlags, appView),
+                    context, actualFieldHolder, target.accessFlags, appView),
                 appView);
       }
 
       ConstraintWithTarget classConstraintWithTarget =
           ConstraintWithTarget.deriveConstraint(
-              invocationContext, fieldHolder, fieldClass.accessFlags, appView);
+              context, fieldHolder, fieldClass.accessFlags, appView);
       return ConstraintWithTarget.meet(
           fieldConstraintWithTarget, classConstraintWithTarget, appView);
     }
@@ -374,7 +359,7 @@
   }
 
   private ConstraintWithTarget forSingleTargetInvoke(
-      DexMethod method, DexEncodedMethod target, DexType invocationContext) {
+      DexMethod method, DexEncodedMethod target, DexProgramClass context) {
     if (method.holder.isArrayType()) {
       return ConstraintWithTarget.ALWAYS;
     }
@@ -389,11 +374,11 @@
 
         ConstraintWithTarget methodConstraintWithTarget =
             ConstraintWithTarget.deriveConstraint(
-                invocationContext, methodHolder, target.accessFlags, appView);
+                context, methodHolder, target.accessFlags, appView);
         // We also have to take the constraint of the enclosing class into account.
         ConstraintWithTarget classConstraintWithTarget =
             ConstraintWithTarget.deriveConstraint(
-                invocationContext, methodHolder, methodClass.accessFlags, appView);
+                context, methodHolder, methodClass.accessFlags, appView);
         return ConstraintWithTarget.meet(
             methodConstraintWithTarget, classConstraintWithTarget, appView);
       }
@@ -402,17 +387,14 @@
   }
 
   private ConstraintWithTarget forVirtualInvoke(
-      DexMethod method,
-      DexType invocationContext,
-      boolean isInterface) {
+      DexMethod method, DexProgramClass context, boolean isInterface) {
     if (method.holder.isArrayType()) {
       return ConstraintWithTarget.ALWAYS;
     }
 
     // Perform resolution and derive inlining constraints based on the accessibility of the
     // resolution result.
-    ResolutionResult resolutionResult =
-        appView.appInfo().resolveMethod(method.holder, method, isInterface);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, isInterface);
     if (!resolutionResult.isVirtualTarget()) {
       return ConstraintWithTarget.NEVER;
     }
@@ -428,7 +410,7 @@
     assert methodClass != null;
     ConstraintWithTarget methodConstraintWithTarget =
         ConstraintWithTarget.deriveConstraint(
-            invocationContext, methodHolder, resolutionTarget.accessFlags, appView);
+            context, methodHolder, resolutionTarget.accessFlags, appView);
     // We also have to take the constraint of the enclosing class of the resolution result
     // into account. We do not allow inlining this method if it is calling something that
     // is inaccessible. Inlining in that case could move the code to another package making a
@@ -436,7 +418,7 @@
     // we have to make sure that inlining cannot make it inaccessible.
     ConstraintWithTarget classConstraintWithTarget =
         ConstraintWithTarget.deriveConstraint(
-            invocationContext, methodHolder, methodClass.accessFlags, appView);
+            context, methodHolder, methodClass.accessFlags, appView);
     return ConstraintWithTarget.meet(
         methodConstraintWithTarget, classConstraintWithTarget, appView);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 458f4b1..8222958 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -146,7 +146,7 @@
       DexField field = returnValueRule.getField();
       assert instruction.getOutType() == TypeElement.fromDexType(field.type, maybeNull(), appView);
 
-      DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.holder, field);
+      DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field);
       if (staticField == null) {
         if (warnedFields.add(field)) {
           reporter.warning(
@@ -212,7 +212,7 @@
       if (current.isStaticGet()) {
         StaticGet staticGet = current.asStaticGet();
         replaceInstructionByInitClassIfPossible(
-            staticGet, staticGet.getField().holder, code, iterator, code.method().holder());
+            staticGet, staticGet.getField().holder, code, iterator, code.context());
       }
       replacement.setPosition(position);
       if (block.hasCatchHandlers()) {
@@ -236,7 +236,7 @@
     if (!invokedHolder.isClassType()) {
       return;
     }
-    DexEncodedMethod target = current.lookupSingleTarget(appView, context.getHolderType());
+    DexEncodedMethod target = current.lookupSingleTarget(appView, context);
     if (target != null && target.isInstanceInitializer()) {
       // Member value propagation does not apply to constructors. Removing a call to a constructor
       // that is marked as having no side effects could lead to verification errors, due to
@@ -250,7 +250,7 @@
       // references that have actual definitions are marked by the root set builder. So, here, we
       // try again with a resolved target, not the direct definition, which may not exist.
       DexEncodedMethod resolutionTarget =
-          appView.appInfo().resolveMethod(invokedHolder, invokedMethod).getSingleTarget();
+          appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).getSingleTarget();
       lookup = lookupMemberRule(resolutionTarget);
     }
     boolean invokeReplaced = false;
@@ -303,10 +303,10 @@
         current.setOutValue(null);
 
         if (current.isInvokeMethodWithReceiver()) {
-          replaceInstructionByNullCheckIfPossible(current, iterator, context.getHolderType());
+          replaceInstructionByNullCheckIfPossible(current, iterator, context);
         } else if (current.isInvokeStatic()) {
           replaceInstructionByInitClassIfPossible(
-              current, target.holder(), code, iterator, context.getHolderType());
+              current, target.holder(), code, iterator, context);
         }
 
         // Insert the definition of the replacement.
@@ -330,7 +330,7 @@
     DexField field = current.getField();
 
     // TODO(b/123857022): Should be able to use definitionFor().
-    DexEncodedField target = appView.appInfo().resolveField(field);
+    DexEncodedField target = appView.appInfo().resolveField(field).getResolvedField();
     if (target == null) {
       boolean replaceCurrentInstructionWithConstNull =
           appView.withGeneratedExtensionRegistryShrinker(
@@ -367,7 +367,7 @@
       abstractValue = target.getOptimizationInfo().getAbstractValue();
       if (abstractValue.isUnknown() && !target.isStatic()) {
         AbstractValue abstractReceiverValue =
-            current.asInstanceGet().object().getAbstractValue(appView, code.method().holder());
+            current.asInstanceGet().object().getAbstractValue(appView, code.context());
         if (abstractReceiverValue.isSingleFieldValue()) {
           abstractValue =
               abstractReceiverValue.asSingleFieldValue().getState().getAbstractFieldValue(target);
@@ -393,7 +393,7 @@
       }
       if (singleValue.isMaterializableInContext(appView, code.context())) {
         BasicBlock block = current.getBlock();
-        DexType context = code.method().holder();
+        ProgramMethod context = code.context();
         Position position = current.getPosition();
 
         // All usages are replaced by the replacement value.
@@ -425,7 +425,7 @@
   }
 
   private void replaceInstructionByNullCheckIfPossible(
-      Instruction instruction, InstructionListIterator iterator, DexType context) {
+      Instruction instruction, InstructionListIterator iterator, ProgramMethod context) {
     assert instruction.isInstanceFieldInstruction() || instruction.isInvokeMethodWithReceiver();
     assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
     if (instruction.instructionMayHaveSideEffects(
@@ -456,7 +456,7 @@
       DexType holder,
       IRCode code,
       InstructionListIterator iterator,
-      DexType context) {
+      ProgramMethod context) {
     assert instruction.isStaticFieldInstruction() || instruction.isInvokeStatic();
     if (instruction.instructionMayHaveSideEffects(
         appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
@@ -467,7 +467,7 @@
             appView,
             // Types that are a super type of `context` are guaranteed to be initialized
             // already.
-            type -> appView.isSubtype(context, type).isTrue(),
+            type -> appView.appInfo().isSubtype(context.getHolderType(), type),
             Sets.newIdentityHashSet());
     if (!classInitializationMayHaveSideEffects) {
       iterator.removeOrReplaceByDebugLocalRead();
@@ -485,7 +485,7 @@
 
   private void replaceInstancePutByNullCheckIfNeverRead(
       IRCode code, InstructionListIterator iterator, InstancePut current) {
-    DexEncodedField target = appView.appInfo().resolveField(current.getField());
+    DexEncodedField target = appView.appInfo().resolveField(current.getField()).getResolvedField();
     if (target == null || appView.appInfo().isFieldRead(target)) {
       return;
     }
@@ -494,12 +494,12 @@
       return;
     }
 
-    replaceInstructionByNullCheckIfPossible(current, iterator, code.method().holder());
+    replaceInstructionByNullCheckIfPossible(current, iterator, code.context());
   }
 
   private void replaceStaticPutByInitClassIfNeverRead(
       IRCode code, InstructionListIterator iterator, StaticPut current) {
-    DexEncodedField field = appView.appInfo().resolveField(current.getField());
+    DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
     if (field == null || appView.appInfo().isFieldRead(field)) {
       return;
     }
@@ -509,7 +509,7 @@
     }
 
     replaceInstructionByInitClassIfPossible(
-        current, field.holder(), code, iterator, code.method().holder());
+        current, field.holder(), code, iterator, code.context());
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 662eb39..406c09c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -5,11 +5,11 @@
 
 import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 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.DexMethod;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -43,17 +43,17 @@
 
 public class NonNullTracker implements Assumer {
 
-  private final AppView<?> appView;
-  private final DexItemFactory dexItemFactory;
+  private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final Consumer<BasicBlock> splitBlockConsumer;
 
-  public NonNullTracker(AppView<?> appView) {
+  public NonNullTracker(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this(appView, null);
   }
 
-  public NonNullTracker(AppView<?> appView, Consumer<BasicBlock> splitBlockConsumer) {
+  public NonNullTracker(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Consumer<BasicBlock> splitBlockConsumer) {
     this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
     this.splitBlockConsumer = splitBlockConsumer;
   }
 
@@ -89,8 +89,7 @@
           InvokeMethod invoke = current.asInvokeMethod();
           DexMethod invokedMethod = invoke.getInvokedMethod();
 
-          DexEncodedMethod singleTarget =
-              invoke.lookupSingleTarget(appView, code.method().holder());
+          DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
           if (singleTarget != null) {
             MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
 
@@ -119,7 +118,7 @@
           FieldInstruction fieldInstruction = current.asFieldInstruction();
           DexField field = fieldInstruction.getField();
           if (field.type.isReferenceType() && isNullableReferenceTypeWithUsers(outValue)) {
-            DexEncodedField encodedField = appView.appInfo().resolveField(field);
+            DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
             if (encodedField != null) {
               FieldOptimizationInfo optimizationInfo = encodedField.getOptimizationInfo();
               if (optimizationInfo.getDynamicUpperBoundType() != null
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 2b9456a..5c4197c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -66,10 +66,14 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ProgramMethodEquivalence;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -108,7 +112,7 @@
 public class Outliner {
 
   /** Result of first step (see {@link Outliner#createOutlineMethodIdentifierGenerator()}. */
-  private final List<List<ProgramMethod>> candidateMethodLists = new ArrayList<>();
+  private final List<Multiset<Wrapper<ProgramMethod>>> candidateMethodLists = new ArrayList<>();
   /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
   private final LongLivedProgramMethodSetBuilder methodsSelectedForOutlining =
       new LongLivedProgramMethodSetBuilder();
@@ -863,8 +867,7 @@
 
       // See whether we could move this invoke somewhere else. We reuse the logic from inlining
       // here, as the constraints are the same.
-      ConstraintWithTarget constraint =
-          invoke.inliningConstraint(inliningConstraints, method.getHolderType());
+      ConstraintWithTarget constraint = invoke.inliningConstraint(inliningConstraints, method);
       if (constraint != ConstraintWithTarget.ALWAYS) {
         return false;
       }
@@ -1135,10 +1138,12 @@
   // TODO(sgjesse): This does not take several usages in the same method into account.
   private class OutlineMethodIdentifier extends OutlineSpotter {
 
-    private final Map<Outline, List<ProgramMethod>> candidateMap;
+    private final Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap;
 
     OutlineMethodIdentifier(
-        ProgramMethod method, BasicBlock block, Map<Outline, List<ProgramMethod>> candidateMap) {
+        ProgramMethod method,
+        BasicBlock block,
+        Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap) {
       super(method, block);
       this.candidateMap = candidateMap;
     }
@@ -1146,12 +1151,14 @@
     @Override
     protected void handle(int start, int end, Outline outline) {
       synchronized (candidateMap) {
-        candidateMap.computeIfAbsent(outline, this::addOutlineMethodList).add(method);
+        candidateMap
+            .computeIfAbsent(outline, this::addOutlineMethodList)
+            .add(ProgramMethodEquivalence.get().wrap(method));
       }
     }
 
-    private List<ProgramMethod> addOutlineMethodList(Outline outline) {
-      List<ProgramMethod> result = new ArrayList<>();
+    private Multiset<Wrapper<ProgramMethod>> addOutlineMethodList(Outline outline) {
+      Multiset<Wrapper<ProgramMethod>> result = HashMultiset.create();
       candidateMethodLists.add(result);
       return result;
     }
@@ -1277,7 +1284,7 @@
     // out-value of invokes to null), this map must not be used except for identifying methods
     // potentially relevant to outlining. OutlineMethodIdentifier will add method lists to
     // candidateMethodLists whenever it adds an entry to candidateMap.
-    Map<Outline, List<ProgramMethod>> candidateMap = new HashMap<>();
+    Map<Outline, Multiset<Wrapper<ProgramMethod>>> candidateMap = new HashMap<>();
     assert candidateMethodLists.isEmpty();
     assert outlineMethodIdentifierGenerator == null;
     outlineMethodIdentifierGenerator =
@@ -1311,9 +1318,9 @@
   public boolean selectMethodsForOutlining() {
     assert methodsSelectedForOutlining.isEmpty();
     assert outlineSites.isEmpty();
-    for (List<ProgramMethod> outlineMethods : candidateMethodLists) {
+    for (Multiset<Wrapper<ProgramMethod>> outlineMethods : candidateMethodLists) {
       if (outlineMethods.size() >= appView.options().outline.threshold) {
-        methodsSelectedForOutlining.addAll(outlineMethods);
+        outlineMethods.forEach(wrapper -> methodsSelectedForOutlining.add(wrapper.get()));
       }
     }
     candidateMethodLists.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 1ba460e..ef81097 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -152,7 +152,7 @@
 
   private DexEncodedField resolveField(DexField field) {
     if (appView.enableWholeProgramOptimizations()) {
-      return appView.appInfo().resolveField(field);
+      return appView.appInfo().withLiveness().resolveField(field).getResolvedField();
     }
     if (field.holder == method.getHolderType()) {
       return appView.definitionFor(field);
@@ -161,7 +161,6 @@
   }
 
   public void run() {
-    DexType context = method.getHolderType();
     Reference2IntMap<BasicBlock> pendingNormalSuccessors = new Reference2IntOpenHashMap<>();
     for (BasicBlock block : code.blocks) {
       if (!block.hasUniqueNormalSuccessor()) {
@@ -274,14 +273,14 @@
                 appView,
                 // Types that are a super type of `context` are guaranteed to be initialized
                 // already.
-                type -> appView.isSubtype(context, type).isTrue(),
+                type -> appView.isSubtype(method.getHolderType(), type).isTrue(),
                 Sets.newIdentityHashSet())) {
               killAllNonFinalActiveFields();
             }
           } else {
             // If the current instruction could trigger a method invocation, it could also cause
             // field values to change. In that case, it must be handled above.
-            assert !instruction.instructionMayTriggerMethodInvocation(appView, context);
+            assert !instruction.instructionMayTriggerMethodInvocation(appView, method);
 
             // If this assertion fails for a new instruction we need to determine if that
             // instruction has side-effects that can change the value of fields. If so, it must be
@@ -348,7 +347,7 @@
       return;
     }
 
-    DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType());
+    DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
     if (singleTarget == null || !singleTarget.isInstanceInitializer()) {
       killAllNonFinalActiveFields();
       return;
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 1f410ef..fd270a4 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
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -39,7 +40,7 @@
       return;
     }
     Set<Value> affectedValues = Sets.newIdentityHashSet();
-    DexType context = code.method().holder();
+    ProgramMethod context = code.context();
     ClassInitializationAnalysis classInitializationAnalysis =
         new ClassInitializationAnalysis(appView, code);
     for (BasicBlock block : code.blocks) {
@@ -84,9 +85,7 @@
   }
 
   private static DexType getTypeForGetClass(
-      AppView<AppInfoWithLiveness> appView,
-      DexType context,
-      InvokeVirtual invoke) {
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context, InvokeVirtual invoke) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexMethod invokedMethod = invoke.getInvokedMethod();
     // Class<?> Object#getClass() is final and cannot be overridden.
@@ -126,7 +125,7 @@
     }
     // Make sure the target (base) type is visible.
     ConstraintWithTarget constraints =
-        ConstraintWithTarget.classIsVisible(context, baseType, appView);
+        ConstraintWithTarget.classIsVisible(context.getHolder(), baseType, appView);
     if (constraints == ConstraintWithTarget.NEVER) {
       return null;
     }
@@ -136,7 +135,7 @@
   private static DexType getTypeForClassForName(
       AppView<AppInfoWithLiveness> appView,
       ClassInitializationAnalysis classInitializationAnalysis,
-      DexType context,
+      ProgramMethod context,
       InvokeStatic invoke) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexMethod invokedMethod = invoke.getInvokedMethod();
@@ -203,7 +202,7 @@
     }
     // Make sure the (base) type is visible.
     ConstraintWithTarget constraints =
-        ConstraintWithTarget.classIsVisible(context, baseType, appView);
+        ConstraintWithTarget.classIsVisible(context.getHolder(), baseType, appView);
     if (constraints == ConstraintWithTarget.NEVER) {
       return null;
     }
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 055f575..32bea32 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
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
@@ -351,8 +352,7 @@
         Instruction instruction = instructionIterator.next();
         if (instruction.throwsOnNullInput()) {
           Value couldBeNullValue = instruction.getNonNullInput();
-          if (isThrowNullCandidate(
-              couldBeNullValue, instruction, appView, code.method().holder())) {
+          if (isThrowNullCandidate(couldBeNullValue, instruction, appView, code.context())) {
             if (instruction.isInstanceGet() || instruction.isInstancePut()) {
               ++numberOfInstanceGetOrInstancePutWithNullReceiver;
             } else if (instruction.isInvokeMethodWithReceiver()) {
@@ -405,7 +405,7 @@
       Value couldBeNullValue,
       Instruction current,
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      DexType context) {
+      ProgramMethod context) {
     if (!couldBeNullValue.isAlwaysNull(appView)) {
       return false;
     }
@@ -451,12 +451,12 @@
       IRCode code,
       AssumeDynamicTypeRemover assumeDynamicTypeRemover,
       Set<Value> affectedValues) {
-    DexType context = code.method().holder();
+    ProgramMethod context = code.context();
     DexField field = instruction.getField();
     DexType fieldType = field.type;
     if (fieldType.isAlwaysNull(appView)) {
       // TODO(b/123857022): Should be possible to use definitionFor().
-      DexEncodedField encodedField = appView.appInfo().resolveField(field);
+      DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
       if (encodedField == null) {
         return;
       }
@@ -507,7 +507,7 @@
       AssumeDynamicTypeRemover assumeDynamicTypeRemover,
       Set<BasicBlock> blocksToBeRemoved,
       Set<Value> affectedValues) {
-    DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method().holder());
+    DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.context());
     if (target == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 59e9a94..9318acb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -172,10 +172,10 @@
     assert root.isStaticGet();
 
     StaticGet staticGet = root.asStaticGet();
-    if (staticGet.instructionMayHaveSideEffects(appView, method.getHolderType())) {
+    if (staticGet.instructionMayHaveSideEffects(appView, method)) {
       return EligibilityStatus.RETRIEVAL_MAY_HAVE_SIDE_EFFECTS;
     }
-    DexEncodedField field = appView.appInfo().resolveField(staticGet.getField());
+    DexEncodedField field = appView.appInfo().resolveField(staticGet.getField()).getResolvedField();
     FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
     ClassTypeElement dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
     if (dynamicLowerBoundType == null
@@ -221,7 +221,14 @@
     while (!currentUsers.isEmpty()) {
       Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
       for (Instruction user : currentUsers) {
-        if (user.isAssume()) {
+        if (user.isAssume() || user.isCheckCast()) {
+          if (user.isCheckCast()) {
+            boolean isCheckCastUnsafe =
+                !appView.appInfo().isSubtype(eligibleClass.type, user.asCheckCast().getType());
+            if (isCheckCastUnsafe) {
+              return user; // Not eligible.
+            }
+          }
           Value alias = user.outValue();
           if (receivers.isReceiverAlias(alias)) {
             continue; // Already processed.
@@ -242,7 +249,10 @@
             return user; // Not eligible.
           }
           DexEncodedField field =
-              appView.appInfo().resolveField(user.asFieldInstruction().getField());
+              appView
+                  .appInfo()
+                  .resolveField(user.asFieldInstruction().getField())
+                  .getResolvedField();
           if (field == null || field.isStatic()) {
             return user; // Not eligible.
           }
@@ -258,7 +268,10 @@
             return user; // Not eligible.
           }
           DexEncodedField field =
-              appView.appInfo().resolveField(user.asFieldInstruction().getField());
+              appView
+                  .appInfo()
+                  .resolveField(user.asFieldInstruction().getField())
+                  .getResolvedField();
           if (field == null || field.isStatic()) {
             return user; // Not eligible.
           }
@@ -267,8 +280,7 @@
 
         if (user.isInvokeMethod()) {
           InvokeMethod invokeMethod = user.asInvokeMethod();
-          DexEncodedMethod singleTargetMethod =
-              invokeMethod.lookupSingleTarget(appView, method.getHolderType());
+          DexEncodedMethod singleTargetMethod = invokeMethod.lookupSingleTarget(appView, method);
           if (singleTargetMethod == null) {
             return user; // Not eligible.
           }
@@ -513,7 +525,8 @@
             continue;
           }
 
-          ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, method);
+          ProgramMethod singleTarget =
+              invoke.lookupSingleProgramTarget(appView, method, eligibleInstance);
           if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) {
             throw new IllegalClassInlinerStateException();
           }
@@ -574,7 +587,7 @@
           continue;
         }
 
-        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType());
+        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
         if (singleTarget != null) {
           Predicate<InvokeMethod> noSideEffectsPredicate =
               dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault(
@@ -701,7 +714,10 @@
       }
       InstancePut instancePut = user.asInstancePut();
       DexEncodedField field =
-          appView.appInfo().resolveFieldOn(eligibleClass, instancePut.getField());
+          appView
+              .appInfo()
+              .resolveFieldOn(eligibleClass, instancePut.getField())
+              .getResolvedField();
       if (field == null) {
         throw new Unreachable(
             "Unexpected field write left in method `"
@@ -880,8 +896,8 @@
         invoke,
         invoke.getInvokedMethod(),
         singleTarget,
-        eligibility ->
-            isEligibleInvokeWithAllUsersAsReceivers(eligibility, invoke, indirectUsers))) {
+        eligibility -> isEligibleInvokeWithAllUsersAsReceivers(eligibility, invoke, indirectUsers),
+        invoke.getType())) {
       return null;
     }
 
@@ -892,15 +908,18 @@
     }
     if (!eligibility.callsReceiver.isEmpty()) {
       assert eligibility.callsReceiver.get(0).getFirst() == Invoke.Type.VIRTUAL;
-      DexMethod indirectlyInvokedMethod = eligibility.callsReceiver.get(0).getSecond();
+      Pair<Type, DexMethod> invokeInfo = eligibility.callsReceiver.get(0);
+      Type invokeType = invokeInfo.getFirst();
+      DexMethod indirectlyInvokedMethod = invokeInfo.getSecond();
       ResolutionResult resolutionResult =
-          appView.appInfo().resolveMethod(eligibleClass, indirectlyInvokedMethod);
+          appView.appInfo().resolveMethodOn(eligibleClass, indirectlyInvokedMethod);
       if (!resolutionResult.isSingleResolution()) {
         return null;
       }
       ProgramMethod indirectSingleTarget =
           resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod();
-      if (!isEligibleIndirectVirtualMethodCall(indirectlyInvokedMethod, indirectSingleTarget)) {
+      if (!isEligibleIndirectVirtualMethodCall(
+          indirectlyInvokedMethod, invokeType, indirectSingleTarget)) {
         return null;
       }
       indirectMethodCallsOnInstance.add(indirectSingleTarget);
@@ -909,16 +928,16 @@
     return new InliningInfo(singleTarget, eligibleClass.type);
   }
 
-  private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod) {
+  private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod, Type type) {
     ProgramMethod singleTarget =
         asProgramMethodOrNull(
-            appView.appInfo().resolveMethod(eligibleClass, invokedMethod).getSingleTarget(),
+            appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).getSingleTarget(),
             appView);
-    return isEligibleIndirectVirtualMethodCall(invokedMethod, singleTarget);
+    return isEligibleIndirectVirtualMethodCall(invokedMethod, type, singleTarget);
   }
 
   private boolean isEligibleIndirectVirtualMethodCall(
-      DexMethod invokedMethod, ProgramMethod singleTarget) {
+      DexMethod invokedMethod, Type type, ProgramMethod singleTarget) {
     if (!isEligibleSingleTarget(singleTarget)) {
       return false;
     }
@@ -929,21 +948,23 @@
         null,
         invokedMethod,
         singleTarget,
-        eligibility ->
-            eligibility.callsReceiver.isEmpty() && eligibility.returnsReceiver.isFalse());
+        eligibility -> eligibility.callsReceiver.isEmpty() && eligibility.returnsReceiver.isFalse(),
+        type);
   }
 
   private boolean isEligibleVirtualMethodCall(
       InvokeMethodWithReceiver invoke,
       DexMethod callee,
       ProgramMethod singleTarget,
-      Predicate<ClassInlinerEligibilityInfo> eligibilityAcceptanceCheck) {
+      Predicate<ClassInlinerEligibilityInfo> eligibilityAcceptanceCheck,
+      Type type) {
     assert isEligibleSingleTarget(singleTarget);
 
     // We should not inline a method if the invocation has type interface or virtual and the
     // signature of the invocation resolves to a private or static method.
     // TODO(b/147212189): Why not inline private methods? If access is permitted it is valid.
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(callee.holder, callee);
+    ResolutionResult resolutionResult =
+        appView.appInfo().resolveMethod(callee, type == Type.INTERFACE);
     if (resolutionResult.isSingleResolution()
         && !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) {
       return false;
@@ -1152,7 +1173,7 @@
 
       if (type == Type.VIRTUAL || type == Type.INTERFACE) {
         // Is the method called indirectly still eligible?
-        if (!isEligibleIndirectVirtualMethodCall(target)) {
+        if (!isEligibleIndirectVirtualMethodCall(target, type)) {
           return false;
         }
       } else if (type == Type.DIRECT) {
@@ -1166,7 +1187,7 @@
       }
 
       // Check if the method is inline-able by standard inliner.
-      DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType());
+      DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
       if (singleTarget == null) {
         return false;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 06c2894..b601000 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -394,7 +394,7 @@
         return Reason.INVALID_INVOKE_ON_ARRAY;
       }
       DexEncodedMethod encodedSingleTarget =
-          invokeMethod.lookupSingleTarget(appView, code.method().holder());
+          invokeMethod.lookupSingleTarget(appView, code.context());
       if (encodedSingleTarget == null) {
         return Reason.INVALID_INVOKE;
       }
@@ -459,7 +459,8 @@
     // have identical enum type.
     if (instruction.isFieldPut()) {
       FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-      DexEncodedField field = appView.appInfo().resolveField(fieldInstruction.getField());
+      DexEncodedField field =
+          appView.appInfo().resolveField(fieldInstruction.getField()).getResolvedField();
       if (field == null) {
         return Reason.INVALID_FIELD_PUT;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 97d0029..1d6668c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -114,7 +114,7 @@
           DexEncodedMethod singleTarget =
               appView
                   .appInfo()
-                  .resolveMethodOnClass(valueInfo.type, factory.objectMembers.toString)
+                  .resolveMethodOnClass(factory.objectMembers.toString, valueInfo.type)
                   .getSingleTarget();
           if (singleTarget != null && singleTarget.method != factory.enumMethods.toString) {
             continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index f4d341b..86d107a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -6,15 +6,16 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.List;
@@ -153,12 +154,10 @@
   }
 
   public static CallSiteOptimizationInfo fromArguments(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      DexEncodedMethod method,
-      List<Value> inValues) {
+      AppView<AppInfoWithLiveness> appView, ProgramMethod target, List<Value> inValues) {
     boolean allowConstantPropagation = appView.options().enablePropagationOfConstantsAtCallSites;
     ConcreteCallSiteOptimizationInfo newCallSiteInfo =
-        new ConcreteCallSiteOptimizationInfo(method, allowConstantPropagation);
+        new ConcreteCallSiteOptimizationInfo(target.getDefinition(), allowConstantPropagation);
     assert newCallSiteInfo.size == inValues.size();
     assert newCallSiteInfo.dynamicUpperBoundTypes != null;
     for (int i = 0; i < newCallSiteInfo.size; i++) {
@@ -167,8 +166,7 @@
         assert newCallSiteInfo.constants != null;
         Value aliasedValue = arg.getAliasedValue();
         if (!aliasedValue.isPhi()) {
-          AbstractValue abstractValue =
-              aliasedValue.definition.getAbstractValue(appView, method.holder());
+          AbstractValue abstractValue = aliasedValue.definition.getAbstractValue(appView, target);
           if (abstractValue.isNonTrivial()) {
             newCallSiteInfo.constants.put(i, abstractValue);
           }
@@ -181,7 +179,7 @@
       assert arg.getType().isReferenceType();
       newCallSiteInfo.dynamicUpperBoundTypes.put(i, arg.getDynamicUpperBoundType(appView));
     }
-    if (newCallSiteInfo.hasUsefulOptimizationInfo(appView, method)) {
+    if (newCallSiteInfo.hasUsefulOptimizationInfo(appView, target.getDefinition())) {
       return newCallSiteInfo;
     }
     // As soon as we know the current call site does not have any useful optimization info,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 23b74b1..8bb40e0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -44,13 +44,14 @@
 import static com.android.tools.r8.ir.code.Opcodes.XOR;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 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.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.DeterminismAnalysis;
@@ -128,9 +129,9 @@
       InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos,
       Timing timing) {
     identifyBridgeInfo(method, code, feedback, timing);
-    identifyClassInlinerEligibility(method, code, feedback, timing);
+    identifyClassInlinerEligibility(code, feedback, timing);
     identifyParameterUsages(method, code, feedback, timing);
-    identifyReturnsArgument(method, code, feedback, timing);
+    identifyReturnsArgument(code, feedback, timing);
     if (options.enableInlining) {
       identifyInvokeSemanticsForInlining(method, code, feedback, timing);
     }
@@ -152,14 +153,13 @@
   }
 
   private void identifyClassInlinerEligibility(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
+      IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify class inliner eligibility");
-    identifyClassInlinerEligibility(method, code, feedback);
+    identifyClassInlinerEligibility(code, feedback);
     timing.end();
   }
 
-  private void identifyClassInlinerEligibility(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+  private void identifyClassInlinerEligibility(IRCode code, OptimizationFeedback feedback) {
     // Method eligibility is calculated in similar way for regular method
     // and for the constructor. To be eligible method should only be using its
     // receiver in the following ways:
@@ -173,24 +173,21 @@
     //
     // Note that (4) can safely be removed as the receiver is guaranteed not to escape when we class
     // inline it, and hence any monitor instructions are no-ops.
-    boolean instanceInitializer = method.isInstanceInitializer();
-    if (method.accessFlags.isNative()
-        || (!method.isNonAbstractVirtualMethod() && !instanceInitializer)) {
+    ProgramMethod context = code.context();
+    DexEncodedMethod definition = context.getDefinition();
+    boolean instanceInitializer = definition.isInstanceInitializer();
+    if (definition.isNative()
+        || (!definition.isNonAbstractVirtualMethod() && !instanceInitializer)) {
       return;
     }
 
-    feedback.setClassInlinerEligibility(method, null);  // To allow returns below.
+    feedback.setClassInlinerEligibility(definition, null); // To allow returns below.
 
     Value receiver = code.getThis();
     if (receiver.numberOfPhiUsers() > 0) {
       return;
     }
 
-    DexClass clazz = appView.definitionFor(method.holder());
-    if (clazz == null) {
-      return;
-    }
-
     List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
     boolean seenSuperInitCall = false;
     boolean seenMonitor = false;
@@ -224,11 +221,10 @@
               }
             }
             DexField field = insn.asFieldInstruction().getField();
-            if (appView.appInfo().resolveFieldOn(clazz, field) != null) {
-              // Require only accessing direct or indirect instance fields of the current class.
-              break;
+            if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
+              return;
             }
-            return;
+            break;
           }
 
         case INVOKE_DIRECT:
@@ -236,7 +232,7 @@
             InvokeDirect invoke = insn.asInvokeDirect();
             DexMethod invokedMethod = invoke.getInvokedMethod();
             if (dexItemFactory.isConstructor(invokedMethod)
-                && invokedMethod.holder == clazz.superType
+                && invokedMethod.holder == context.getHolder().superType
                 && ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) == 0
                 && !seenSuperInitCall
                 && instanceInitializer) {
@@ -250,7 +246,7 @@
         case INVOKE_STATIC:
           {
             InvokeStatic invoke = insn.asInvokeStatic();
-            DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
+            DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
             if (singleTarget == null) {
               return; // Not allowed.
             }
@@ -271,7 +267,7 @@
             DexMethod invokedMethod = invoke.getInvokedMethod();
             DexType returnType = invokedMethod.proto.returnType;
             if (returnType.isClassType()
-                && appView.appInfo().inSameHierarchy(returnType, method.holder())) {
+                && appView.appInfo().inSameHierarchy(returnType, context.getHolderType())) {
               return; // Not allowed, could introduce an alias of the receiver.
             }
             callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
@@ -289,13 +285,13 @@
       return;
     }
 
-    boolean synchronizedVirtualMethod = method.isSynchronized() && method.isVirtualMethod();
+    boolean synchronizedVirtualMethod = definition.isSynchronized() && definition.isVirtualMethod();
 
     feedback.setClassInlinerEligibility(
-        method,
+        definition,
         new ClassInlinerEligibilityInfo(
             callsReceiver,
-            new ClassInlinerReceiverAnalysis(appView, method, code).computeReturnsReceiver(),
+            new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
             seenMonitor || synchronizedVirtualMethod));
   }
 
@@ -345,15 +341,15 @@
     return builder.build();
   }
 
-  private void identifyReturnsArgument(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
+  private void identifyReturnsArgument(IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify returns argument");
-    identifyReturnsArgument(method, code, feedback);
+    identifyReturnsArgument(code, feedback);
     timing.end();
   }
 
-  private void identifyReturnsArgument(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+  private void identifyReturnsArgument(IRCode code, OptimizationFeedback feedback) {
+    ProgramMethod context = code.context();
+    DexEncodedMethod method = context.getDefinition();
     List<BasicBlock> normalExits = code.computeNormalExitBlocks();
     if (normalExits.isEmpty()) {
       feedback.methodNeverReturnsNormally(method);
@@ -380,7 +376,6 @@
         if (definition.isArgument()) {
           feedback.methodReturnsArgument(method, definition.asArgument().getIndex());
         }
-        DexType context = method.holder();
         AbstractValue abstractReturnValue = definition.getAbstractValue(appView, context);
         if (abstractReturnValue.isNonTrivial()) {
           feedback.methodReturnsAbstractValue(method, appView, abstractReturnValue);
@@ -424,16 +419,9 @@
       return;
     }
 
-    DexClass clazz = appView.appInfo().definitionFor(method.holder());
-    if (clazz == null) {
-      assert false;
-      return;
-    }
-
     NonTrivialInstanceInitializerInfo.Builder builder =
         NonTrivialInstanceInitializerInfo.builder(instanceFieldInitializationInfos);
-    InstanceInitializerInfo instanceInitializerInfo =
-        analyzeInstanceInitializer(code, clazz, builder);
+    InstanceInitializerInfo instanceInitializerInfo = analyzeInstanceInitializer(code, builder);
     feedback.setInstanceInitializerInfo(
         method,
         instanceInitializerInfo != null
@@ -457,8 +445,9 @@
   //
   // (Note that this initializer does not have to have zero arguments.)
   private InstanceInitializerInfo analyzeInstanceInitializer(
-      IRCode code, DexClass clazz, NonTrivialInstanceInitializerInfo.Builder builder) {
-    if (clazz.definesFinalizer(options.itemFactory)) {
+      IRCode code, NonTrivialInstanceInitializerInfo.Builder builder) {
+    ProgramMethod context = code.context();
+    if (context.getHolder().definesFinalizer(options.itemFactory)) {
       // Defining a finalize method can observe the side-effect of Object.<init> GC registration.
       return null;
     }
@@ -510,7 +499,7 @@
             // instructions can trigger class initialization side effects, hence it is not necessary
             // to mark all fields as potentially being read. Also, none of the instruction types
             // can cause the receiver to escape.
-            if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
+            if (instruction.instructionMayHaveSideEffects(appView, context)) {
               builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
             }
             break;
@@ -519,12 +508,13 @@
           case STATIC_GET:
             {
               FieldInstruction fieldGet = instruction.asFieldInstruction();
-              DexEncodedField field = appView.appInfo().resolveField(fieldGet.getField());
+              DexEncodedField field =
+                  appView.appInfo().resolveField(fieldGet.getField()).getResolvedField();
               if (field == null) {
                 return null;
               }
               builder.markFieldAsRead(field);
-              if (fieldGet.instructionMayHaveSideEffects(appView, clazz.type)) {
+              if (fieldGet.instructionMayHaveSideEffects(appView, context)) {
                 builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
                 if (fieldGet.isStaticGet()) {
                   // It could trigger a class initializer.
@@ -537,14 +527,15 @@
           case INSTANCE_PUT:
             {
               InstancePut instancePut = instruction.asInstancePut();
-              DexEncodedField field = appView.appInfo().resolveField(instancePut.getField());
+              DexEncodedField field =
+                  appView.appInfo().resolveField(instancePut.getField()).getResolvedField();
               if (field == null) {
                 return null;
               }
               Value object =
                   instancePut.object().getAliasedValue(aliasesThroughAssumeAndCheckCasts);
               if (object != receiver
-                  || instancePut.instructionInstanceCanThrow(appView, clazz.type).isThrowing()) {
+                  || instancePut.instructionInstanceCanThrow(appView, context).isThrowing()) {
                 builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
               }
 
@@ -612,7 +603,7 @@
           case INVOKE_NEW_ARRAY:
             {
               InvokeNewArray invoke = instruction.asInvokeNewArray();
-              if (invoke.instructionMayHaveSideEffects(appView, clazz.type)) {
+              if (invoke.instructionMayHaveSideEffects(appView, context)) {
                 builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
               }
               for (Value argument : invoke.arguments()) {
@@ -644,7 +635,7 @@
           case NEW_INSTANCE:
             {
               NewInstance newInstance = instruction.asNewInstance();
-              if (newInstance.instructionMayHaveSideEffects(appView, clazz.type)) {
+              if (newInstance.instructionMayHaveSideEffects(appView, context)) {
                 // It could trigger a class initializer.
                 builder
                     .markAllFieldsAsRead()
@@ -694,12 +685,12 @@
     if (method.isStatic()) {
       // Identifies if the method preserves class initialization after inlining.
       feedback.markTriggerClassInitBeforeAnySideEffect(
-          method, triggersClassInitializationBeforeSideEffect(method.holder(), code, appView));
+          method, triggersClassInitializationBeforeSideEffect(code));
     } else {
       // Identifies if the method preserves null check of the receiver after inlining.
       final Value receiver = code.getThis();
       feedback.markCheckNullReceiverBeforeAnySideEffect(
-          method, receiver.isUsed() && checksNullBeforeSideEffect(code, receiver, appView));
+          method, receiver.isUsed() && checksNullBeforeSideEffect(code, receiver));
     }
   }
 
@@ -709,14 +700,17 @@
    *
    * <p>Note: we do not track phis so we may return false negative. This is a conservative approach.
    */
-  private static boolean triggersClassInitializationBeforeSideEffect(
-      DexType clazz, IRCode code, AppView<?> appView) {
+  private boolean triggersClassInitializationBeforeSideEffect(IRCode code) {
     return alwaysTriggerExpectedEffectBeforeAnythingElse(
         code,
         (instruction, it) -> {
-          DexType context = code.method().holder();
+          ProgramMethod context = code.context();
           if (instruction.definitelyTriggersClassInitialization(
-              clazz, context, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
+              context.getHolderType(),
+              context,
+              appView,
+              DIRECTLY,
+              AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
             // In order to preserve class initialization semantic, the exception must not be caught
             // by any handler. Therefore, we must ignore this instruction if it is covered by a
             // catch handler.
@@ -726,7 +720,7 @@
               // We found an instruction that preserves initialization of the class.
               return InstructionEffect.DESIRED_EFFECT;
             }
-          } else if (instruction.instructionMayHaveSideEffects(appView, clazz)) {
+          } else if (instruction.instructionMayHaveSideEffects(appView, context)) {
             // We found a side effect before class initialization.
             return InstructionEffect.OTHER_EFFECT;
           }
@@ -810,7 +804,7 @@
    *
    * <p>Note: we do not track phis so we may return false negative. This is a conservative approach.
    */
-  private static boolean checksNullBeforeSideEffect(IRCode code, Value value, AppView<?> appView) {
+  private boolean checksNullBeforeSideEffect(IRCode code, Value value) {
     return alwaysTriggerExpectedEffectBeforeAnythingElse(
         code,
         (instr, it) -> {
@@ -847,7 +841,7 @@
           if (isInstantiationOfNullPointerException(instr, it, appView.dexItemFactory())) {
             it.next(); // Skip call to NullPointerException.<init>.
             return InstructionEffect.NO_EFFECT;
-          } else if (instr.throwsNpeIfValueIsNull(value, appView, code.method().holder())) {
+          } else if (instr.throwsNpeIfValueIsNull(value, appView, code.context())) {
             // In order to preserve NPE semantic, the exception must not be caught by any handler.
             // Therefore, we must ignore this instruction if it is covered by a catch handler.
             // Note: this is a conservative approach where we consider that any catch handler could
@@ -856,7 +850,7 @@
               // We found a NPE check on the value.
               return InstructionEffect.DESIRED_EFFECT;
             }
-          } else if (instr.instructionMayHaveSideEffects(appView, code.method().holder())) {
+          } else if (instr.instructionMayHaveSideEffects(appView, code.context())) {
             // If the current instruction is const-string, this could load the parameter name.
             // Just make sure it is indeed not throwing.
             if (instr.isConstString() && !instr.instructionInstanceCanThrow()) {
@@ -1021,7 +1015,7 @@
     if (appView.appInfo().mayHaveSideEffects.containsKey(method.method)) {
       return;
     }
-    DexType context = method.holder();
+    ProgramMethod context = code.context();
     if (method.isClassInitializer()) {
       // For class initializers, we also wish to compute if the class initializer has observable
       // side effects.
@@ -1033,9 +1027,9 @@
       } else if (classInitializerSideEffect.canBePostponed()) {
         feedback.classInitializerMayBePostponed(method);
       } else {
-        assert !context.isD8R8SynthesizedLambdaClassType()
+        assert !context.getHolderType().isD8R8SynthesizedLambdaClassType()
                 || options.debug
-                || appView.appInfo().hasPinnedInstanceInitializer(context)
+                || appView.appInfo().hasPinnedInstanceInitializer(context.getHolderType())
             : "Unexpected observable side effects from lambda `" + context.toSourceString() + "`";
       }
       return;
@@ -1044,7 +1038,7 @@
     if (method.isSynchronized()) {
       // If the method is synchronized then it acquires a lock.
       mayHaveSideEffects = true;
-    } else if (method.isInstanceInitializer() && hasNonTrivialFinalizeMethod(context)) {
+    } else if (method.isInstanceInitializer() && hasNonTrivialFinalizeMethod(context.getHolder())) {
       // If a class T overrides java.lang.Object.finalize(), then treat the constructor as having
       // side effects. This ensures that we won't remove instructions on the form `new-instance
       // {v0}, T`.
@@ -1064,31 +1058,20 @@
     }
   }
 
-  // Returns true if `method` is an initializer and the enclosing class overrides the method
-  // `void java.lang.Object.finalize()`.
-  private boolean hasNonTrivialFinalizeMethod(DexType type) {
-    DexClass clazz = appView.definitionFor(type);
-    if (clazz != null) {
-      if (clazz.isProgramClass() && !clazz.isInterface()) {
-        DexItemFactory dexItemFactory = appView.dexItemFactory();
-        ResolutionResult resolutionResult =
-            appView
-                .appInfo()
-                .resolveMethodOnClass(clazz, appView.dexItemFactory().objectMembers.finalize);
-
-        DexEncodedMethod target = resolutionResult.getSingleTarget();
-        if (target != null
-            && target.method != dexItemFactory.enumMethods.finalize
-            && target.method != dexItemFactory.objectMembers.finalize) {
-          return true;
-        }
-        return false;
-      } else {
-        // Conservatively report that the library class could implement finalize().
-        return true;
-      }
+  // Returns true if the given class overrides the method `void java.lang.Object.finalize()`.
+  private boolean hasNonTrivialFinalizeMethod(DexProgramClass clazz) {
+    if (clazz.isInterface()) {
+      return false;
     }
-    return false;
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    ResolutionResult resolutionResult =
+        appView
+            .appInfo()
+            .resolveMethodOnClass(appView.dexItemFactory().objectMembers.finalize, clazz);
+    DexEncodedMethod target = resolutionResult.getSingleTarget();
+    return target != null
+        && target.method != dexItemFactory.enumMethods.finalize
+        && target.method != dexItemFactory.objectMembers.finalize;
   }
 
   private void computeReturnValueOnlyDependsOnArguments(
@@ -1137,7 +1120,7 @@
       //   invoke-static throwParameterIsNullException(msg)
       //
       // or some other variants, e.g., throw null or NPE after the direct null check.
-      if (argument.isUsed() && checksNullBeforeSideEffect(code, argument, appView)) {
+      if (argument.isUsed() && checksNullBeforeSideEffect(code, argument)) {
         paramsCheckedForNull.set(index);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 091a5e5..05d9b49 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -5,21 +5,21 @@
 package com.android.tools.r8.ir.optimize.inliner;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 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.InvokeMethod;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Collection;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.Set;
 
 public abstract class WhyAreYouNotInliningReporter {
 
   public static WhyAreYouNotInliningReporter createFor(
-      DexEncodedMethod callee, AppView<AppInfoWithLiveness> appView, DexEncodedMethod context) {
-    if (appView.appInfo().whyAreYouNotInlining.contains(callee.method)) {
+      ProgramMethod callee, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+    if (appView.appInfo().whyAreYouNotInlining.contains(callee.getReference())) {
       return new WhyAreYouNotInliningReporterImpl(
           callee, context, appView.options().testing.whyAreYouNotInliningConsumer);
     }
@@ -27,20 +27,20 @@
   }
 
   public static void handleInvokeWithUnknownTarget(
-      InvokeMethod invoke, AppView<AppInfoWithLiveness> appView, DexEncodedMethod context) {
+      InvokeMethod invoke, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     if (appView.appInfo().whyAreYouNotInlining.isEmpty()) {
       return;
     }
 
-    Collection<DexEncodedMethod> possibleTargets = invoke.lookupTargets(appView, context.holder());
-    if (possibleTargets == null) {
+    ProgramMethodSet possibleProgramTargets = invoke.lookupProgramDispatchTargets(appView, context);
+    if (possibleProgramTargets == null) {
       // In principle, this invoke might target any method in the program, but we do not want to
       // report a message for each of the methods in `AppInfoWithLiveness#whyAreYouNotInlining`,
       // since that would almost never be useful.
       return;
     }
 
-    for (DexEncodedMethod possibleTarget : possibleTargets) {
+    for (ProgramMethod possibleTarget : possibleProgramTargets) {
       createFor(possibleTarget, appView, context).reportUnknownTarget();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index b141817..934651c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
@@ -15,14 +15,14 @@
 
 class WhyAreYouNotInliningReporterImpl extends WhyAreYouNotInliningReporter {
 
-  private final DexEncodedMethod callee;
-  private final DexEncodedMethod context;
+  private final ProgramMethod callee;
+  private final ProgramMethod context;
   private final PrintStream output;
 
   private boolean reasonHasBeenReported = false;
 
   WhyAreYouNotInliningReporterImpl(
-      DexEncodedMethod callee, DexEncodedMethod context, PrintStream output) {
+      ProgramMethod callee, ProgramMethod context, PrintStream output) {
     this.callee = callee;
     this.context = context;
     this.output = output;
@@ -30,9 +30,9 @@
 
   private void print(String reason) {
     output.print("Method `");
-    output.print(callee.method.toSourceString());
+    output.print(callee.toSourceString());
     output.print("` was not inlined into `");
-    output.print(context.method.toSourceString());
+    output.print(context.toSourceString());
     if (reason != null) {
       output.print("`: ");
       output.println(reason);
@@ -220,7 +220,7 @@
         "final field `"
             + instancePut.getField()
             + "` must be initialized in a constructor of `"
-            + callee.holder().toSourceString()
+            + callee.getHolderType().toSourceString()
             + "`.");
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 2a5e9cd..7a81f73 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -157,8 +157,7 @@
         DexType holder = invokedMethod.holder;
         if (lambdaGroup.containsLambda(holder)) {
           // TODO(b/150685763): Check if we can use simpler lookup.
-          ResolutionResult resolution =
-              appView.appInfo().resolveMethod(holder, invokedMethod, false);
+          ResolutionResult resolution = appView.appInfo().resolveMethodOnClass(invokedMethod);
           assert resolution.isSingleResolution();
           ProgramMethod singleTarget =
               resolution.asSingleResolution().getResolutionPair().asProgramMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
index 76e81ac..a560844 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -70,7 +70,7 @@
       InvokeMethod invoke,
       Set<Value> affectedValues) {
     Value argument = invoke.arguments().get(0);
-    AbstractValue abstractValue = argument.getAbstractValue(appView, code.method().holder());
+    AbstractValue abstractValue = argument.getAbstractValue(appView, code.context());
     if (abstractValue.isSingleNumberValue()) {
       instructionIterator.replaceCurrentInstructionWithStaticGet(
           appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index 6566187..0c7393b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory.LibraryMembers;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -86,12 +87,12 @@
    *
    * <p>In order for library modeling to work in D8, we return a definition for invoke instructions
    * that are guaranteed to dispatch to a library method in {@link
-   * InvokeMethod#lookupSingleTarget(AppView, DexType)}. As part of that, we bail-out if the holder
-   * of the targeted method is not a library class. However, what is usually on the library path
-   * will be on the program path when compiling the framework itself. To ensure that our library
-   * modeling works also for the framework compilation, we maintain the set of types that we model,
-   * and treat these types as library types in {@link InvokeMethod#lookupSingleTarget(AppView,
-   * DexType)} although they are on the program path.
+   * InvokeMethod#lookupSingleTarget(AppView, ProgramMethod)}. As part of that, we bail-out if the
+   * holder of the targeted method is not a library class. However, what is usually on the library
+   * path will be on the program path when compiling the framework itself. To ensure that our
+   * library modeling works also for the framework compilation, we maintain the set of types that we
+   * model, and treat these types as library types in {@link
+   * InvokeMethod#lookupSingleTarget(AppView, ProgramMethod)} although they are on the program path.
    */
   public boolean isModeled(DexType type) {
     return modeledLibraryTypes.contains(type);
@@ -114,7 +115,7 @@
       Instruction instruction = instructionIterator.next();
       if (instruction.isInvokeMethod()) {
         InvokeMethod invoke = instruction.asInvokeMethod();
-        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method().holder());
+        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
         if (singleTarget != null) {
           optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index 1283bf4..64dce88 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -47,7 +48,7 @@
   private void optimizeEquals(
       IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
     if (appView.appInfo().hasLiveness()) {
-      DexType context = code.method().holder();
+      ProgramMethod context = code.context();
       Value first = invoke.arguments().get(0).getAliasedValue();
       Value second = invoke.arguments().get(1).getAliasedValue();
       if (isPrunedClassNameComparison(first, second, context)
@@ -63,7 +64,7 @@
    * has been pruned by the {@link com.android.tools.r8.shaking.Enqueuer}.
    */
   private boolean isPrunedClassNameComparison(
-      Value classNameValue, Value constStringValue, DexType context) {
+      Value classNameValue, Value constStringValue, ProgramMethod context) {
     if (classNameValue.isPhi() || constStringValue.isPhi()) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 041cf09..acba57f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -82,8 +82,8 @@
       candidates.put(candidate.type, this);
     }
 
-    boolean isHostClassInitializer(DexEncodedMethod method) {
-      return factory.isClassConstructor(method.method) && method.holder() == hostType();
+    boolean isHostClassInitializer(ProgramMethod method) {
+      return method.getDefinition().isClassInitializer() && method.getHolderType() == hostType();
     }
 
     DexType hostType() {
@@ -217,10 +217,10 @@
   //
   // NOTE: can be called concurrently.
   public final void examineMethodCode(IRCode code) {
-    ProgramMethod method = code.context();
+    ProgramMethod context = code.context();
     Set<Instruction> alreadyProcessed = Sets.newIdentityHashSet();
 
-    CandidateInfo receiverClassCandidateInfo = candidates.get(method.getHolderType());
+    CandidateInfo receiverClassCandidateInfo = candidates.get(context.getHolderType());
     Value receiverValue = code.getThis(); // NOTE: is null for static methods.
     if (receiverClassCandidateInfo != null) {
       if (receiverValue != null) {
@@ -230,26 +230,26 @@
         analyzeAllValueUsers(
             receiverClassCandidateInfo,
             receiverValue,
-            factory.isConstructor(method.getReference()));
+            factory.isConstructor(context.getReference()));
 
         // If the candidate is still valid, ignore all instructions
         // we treat as valid usages on receiver.
-        if (candidates.get(method.getHolderType()) != null) {
+        if (candidates.get(context.getHolderType()) != null) {
           alreadyProcessed.addAll(receiverValue.uniqueUsers());
         }
       } else {
         // We are inside a static method of candidate class.
         // Check if this is a valid getter of the singleton field.
-        if (method.getDefinition().returnType() == method.getHolderType()) {
+        if (context.getDefinition().returnType() == context.getHolderType()) {
           List<Instruction> examined = isValidGetter(receiverClassCandidateInfo, code);
           if (examined != null) {
             DexEncodedMethod getter = receiverClassCandidateInfo.getter.get();
             if (getter == null) {
-              receiverClassCandidateInfo.getter.set(method.getDefinition());
+              receiverClassCandidateInfo.getter.set(context.getDefinition());
               // Except for static-get and return, iterate other remaining instructions if any.
               alreadyProcessed.addAll(examined);
             } else {
-              assert getter != method.getDefinition();
+              assert getter != context.getDefinition();
               // Not sure how to deal with many getters.
               receiverClassCandidateInfo.invalidate();
             }
@@ -275,8 +275,7 @@
       if (instruction.isNewInstance()) {
         // Check the class being initialized against valid staticizing candidates.
         NewInstance newInstance = instruction.asNewInstance();
-        CandidateInfo candidateInfo =
-            processInstantiation(method.getDefinition(), iterator, newInstance);
+        CandidateInfo candidateInfo = processInstantiation(context, iterator, newInstance);
         if (candidateInfo != null) {
           alreadyProcessed.addAll(newInstance.outValue().aliasedUsers());
           // For host class initializers having eligible instantiation we also want to
@@ -284,7 +283,7 @@
           // This must guarantee that removing field access will not result in missing side
           // effects, otherwise we can still staticize, but cannot remove singleton reads.
           while (iterator.hasNext()) {
-            if (!isAllowedInHostClassInitializer(method.getHolderType(), iterator.next(), code)) {
+            if (!isAllowedInHostClassInitializer(context.getHolderType(), iterator.next(), code)) {
               candidateInfo.preserveRead.set(true);
               iterator.previous();
               break;
@@ -293,7 +292,7 @@
           }
           referencedFrom
               .computeIfAbsent(candidateInfo, ignore -> new LongLivedProgramMethodSetBuilder())
-              .add(method);
+              .add(context);
         }
         continue;
       }
@@ -315,7 +314,7 @@
         if (info != null) {
           referencedFrom
               .computeIfAbsent(info, ignore -> new LongLivedProgramMethodSetBuilder())
-              .add(method);
+              .add(context);
           // If the candidate is still valid, ignore all usages in further analysis.
           Value value = instruction.outValue();
           if (value != null) {
@@ -331,7 +330,7 @@
         if (info != null) {
           referencedFrom
               .computeIfAbsent(info, ignore -> new LongLivedProgramMethodSetBuilder())
-              .add(method);
+              .add(context);
           // If the candidate is still valid, ignore all usages in further analysis.
           Value value = instruction.outValue();
           if (value != null) {
@@ -380,8 +379,7 @@
   }
 
   private CandidateInfo processInstantiation(
-      DexEncodedMethod method, ListIterator<Instruction> iterator, NewInstance newInstance) {
-
+      ProgramMethod context, ListIterator<Instruction> iterator, NewInstance newInstance) {
     DexType candidateType = newInstance.clazz;
     CandidateInfo candidateInfo = candidates.get(candidateType);
     if (candidateInfo == null) {
@@ -393,7 +391,7 @@
       return candidateInfo.invalidate();
     }
 
-    if (!candidateInfo.isHostClassInitializer(method)) {
+    if (!candidateInfo.isHostClassInitializer(context)) {
       // A valid candidate must only have one instantiation which is
       // done in the static initializer of the host class.
       return candidateInfo.invalidate();
@@ -445,7 +443,7 @@
     }
     Set<Instruction> users = SetUtils.newIdentityHashSet(candidateValue.uniqueUsers());
     Instruction constructorCall = iterator.next();
-    if (!isValidInitCall(candidateInfo, constructorCall, candidateValue, candidateType)) {
+    if (!isValidInitCall(candidateInfo, constructorCall, candidateValue, context)) {
       iterator.previous();
       return candidateInfo.invalidate();
     }
@@ -475,7 +473,7 @@
   }
 
   private boolean isValidInitCall(
-      CandidateInfo info, Instruction instruction, Value candidateValue, DexType candidateType) {
+      CandidateInfo info, Instruction instruction, Value candidateValue, ProgramMethod context) {
     if (!instruction.isInvokeDirect()) {
       return false;
     }
@@ -483,12 +481,12 @@
     // Check constructor.
     InvokeDirect invoke = instruction.asInvokeDirect();
     DexEncodedMethod methodInvoked =
-        appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod(), info.candidate);
+        appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod(), context);
     List<Value> values = invoke.inValues();
 
     if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0
         || methodInvoked == null
-        || methodInvoked.holder() != candidateType) {
+        || methodInvoked.holder() != info.candidate.type) {
       return false;
     }
 
@@ -511,8 +509,7 @@
     }
     // Allow single assignment to a singleton field.
     StaticPut staticPut = instruction.asStaticPut();
-    DexEncodedField fieldAccessed =
-        appView.appInfo().lookupStaticTarget(staticPut.getField().holder, staticPut.getField());
+    DexEncodedField fieldAccessed = appView.appInfo().lookupStaticTarget(staticPut.getField());
     return fieldAccessed == info.singletonField;
   }
 
@@ -529,8 +526,7 @@
     for (Instruction instr : code.instructions()) {
       if (instr.isStaticGet()) {
         staticGet = instr.asStaticGet();
-        DexEncodedField fieldAccessed =
-            appView.appInfo().lookupStaticTarget(staticGet.getField().holder, staticGet.getField());
+        DexEncodedField fieldAccessed = appView.appInfo().lookupStaticTarget(staticGet.getField());
         if (fieldAccessed != info.singletonField) {
           return null;
         }
@@ -562,7 +558,7 @@
       return null;
     }
 
-    assert candidateInfo.singletonField == appView.appInfo().lookupStaticTarget(field.holder, field)
+    assert candidateInfo.singletonField == appView.appInfo().lookupStaticTarget(field)
         : "Added reference after collectCandidates(...)?";
 
     Value singletonValue = staticGet.dest();
@@ -651,7 +647,7 @@
       }
       AppInfoWithLiveness appInfo = appView.appInfo();
       ResolutionResult resolutionResult =
-          appInfo.resolveMethod(methodReferenced.holder, methodReferenced);
+          appInfo.unsafeResolveMethodDueToDexFormat(methodReferenced);
       DexEncodedMethod methodInvoked =
           user.isInvokeDirect()
               ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 07d330b..53122b0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -11,6 +11,7 @@
 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.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysis;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysisConfiguration;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -882,7 +883,7 @@
         AppView<?> appView,
         EscapeAnalysis escapeAnalysis,
         Instruction escapeRoute,
-        DexMethod context) {
+        ProgramMethod context) {
       if (escapeRoute.isReturn() || escapeRoute.isThrow() || escapeRoute.isStaticPut()) {
         logEscapingRoute(false);
         return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index ae94194..d00100a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -17,6 +17,7 @@
 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.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysis;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysisConfiguration;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -589,7 +590,7 @@
         AppView<?> appView,
         EscapeAnalysis escapeAnalysis,
         Instruction escapeRoute,
-        DexMethod context) {
+        ProgramMethod context) {
       if (escapeRoute.isReturn() || escapeRoute.isThrow() || escapeRoute.isStaticPut()) {
         return false;
       }
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 18b3533..b225440 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -192,9 +190,4 @@
     public final DexMethod throwNpe = factory.createMethod(
         type, factory.createProto(factory.voidType), "throwNpe");
   }
-
-  // Calculates kotlin info for a class.
-  public KotlinClassLevelInfo getKotlinInfo(DexClass clazz, AppView<?> appView) {
-    return KotlinClassMetadataReader.getKotlinInfo(this, clazz, appView);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
index 47ed582..a4bc5b0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -4,7 +4,10 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromBinaryName;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
@@ -31,19 +34,21 @@
     this.arguments = arguments;
   }
 
-  private static KotlinAnnotationInfo create(KmAnnotation annotation, AppView<?> appView) {
-    String descriptor = DescriptorUtils.getDescriptorFromClassBinaryName(annotation.getClassName());
-    DexType type = appView.dexItemFactory().createType(descriptor);
-    return new KotlinAnnotationInfo(type, annotation.getArguments());
+  private static KotlinAnnotationInfo create(
+      KmAnnotation annotation, DexDefinitionSupplier definitionSupplier) {
+    return new KotlinAnnotationInfo(
+        referenceTypeFromBinaryName(annotation.getClassName(), definitionSupplier),
+        annotation.getArguments());
   }
 
-  static List<KotlinAnnotationInfo> create(List<KmAnnotation> annotations, AppView<?> appView) {
+  static List<KotlinAnnotationInfo> create(
+      List<KmAnnotation> annotations, DexDefinitionSupplier definitionSupplier) {
     if (annotations.isEmpty()) {
       return EMPTY_ANNOTATIONS;
     }
     ImmutableList.Builder<KotlinAnnotationInfo> builder = ImmutableList.builder();
     for (KmAnnotation annotation : annotations) {
-      builder.add(create(annotation, appView));
+      builder.add(create(annotation, definitionSupplier));
     }
     return builder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index b2f71cb..2cb8fe4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromBinaryName;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexString;
@@ -16,6 +18,7 @@
 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.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.HashMap;
 import java.util.List;
@@ -68,7 +71,11 @@
     this.anonymousObjectOrigin = anonymousObjectOrigin;
   }
 
-  public static KotlinClassInfo create(KmClass kmClass, DexClass hostClass, AppView<?> appView) {
+  public static KotlinClassInfo create(
+      KmClass kmClass,
+      DexClass hostClass,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : hostClass.fields()) {
       fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -79,7 +86,8 @@
     }
     ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
     for (KmConstructor kmConstructor : kmClass.getConstructors()) {
-      KotlinConstructorInfo constructorInfo = KotlinConstructorInfo.create(kmConstructor, appView);
+      KotlinConstructorInfo constructorInfo =
+          KotlinConstructorInfo.create(kmConstructor, definitionSupplier, reporter);
       JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
       if (signature != null) {
         DexEncodedMethod method = methodMap.get(signature.asString());
@@ -92,72 +100,65 @@
       notBackedConstructors.add(constructorInfo);
     }
     KotlinDeclarationContainerInfo container =
-        KotlinDeclarationContainerInfo.create(kmClass, methodMap, fieldMap, appView);
-    setCompanionObject(kmClass, hostClass, appView);
+        KotlinDeclarationContainerInfo.create(
+            kmClass, methodMap, fieldMap, definitionSupplier, reporter);
+    setCompanionObject(kmClass, hostClass, reporter);
     return new KotlinClassInfo(
         kmClass.getFlags(),
         kmClass.name,
         JvmExtensionsKt.getModuleName(kmClass),
         container,
-        KotlinTypeParameterInfo.create(kmClass.getTypeParameters(), appView),
+        KotlinTypeParameterInfo.create(kmClass.getTypeParameters(), definitionSupplier, reporter),
         notBackedConstructors.build(),
-        getSuperTypes(kmClass.getSupertypes(), appView),
-        getSealedSubClasses(hostClass, kmClass.getSealedSubclasses(), appView),
-        getNestedClasses(hostClass, kmClass.getNestedClasses(), appView),
+        getSuperTypes(kmClass.getSupertypes(), definitionSupplier, reporter),
+        getSealedSubClasses(hostClass, kmClass.getSealedSubclasses(), definitionSupplier),
+        getNestedClasses(hostClass, kmClass.getNestedClasses(), definitionSupplier),
         kmClass.getEnumEntries(),
-        getAnonymousObjectOrigin(kmClass, appView));
+        getAnonymousObjectOrigin(kmClass, definitionSupplier));
   }
 
-  private static DexType getAnonymousObjectOrigin(KmClass kmClass, AppView<?> appView) {
+  private static DexType getAnonymousObjectOrigin(
+      KmClass kmClass, DexDefinitionSupplier definitionSupplier) {
     String anonymousObjectOriginName = JvmExtensionsKt.getAnonymousObjectOriginName(kmClass);
     if (anonymousObjectOriginName != null) {
-      return appView
-          .dexItemFactory()
-          .createType(DescriptorUtils.getDescriptorFromClassBinaryName(anonymousObjectOriginName));
+      return referenceTypeFromBinaryName(anonymousObjectOriginName, definitionSupplier);
     }
     return null;
   }
 
   private static List<DexType> getNestedClasses(
-      DexClass clazz, List<String> nestedClasses, AppView<?> appView) {
+      DexClass clazz, List<String> nestedClasses, DexDefinitionSupplier definitionSupplier) {
     ImmutableList.Builder<DexType> nestedTypes = ImmutableList.builder();
     for (String nestedClass : nestedClasses) {
       String binaryName =
           clazz.type.toBinaryName() + DescriptorUtils.INNER_CLASS_SEPARATOR + nestedClass;
-      DexType nestedType =
-          appView
-              .dexItemFactory()
-              .createType(DescriptorUtils.getDescriptorFromClassBinaryName(binaryName));
-      nestedTypes.add(nestedType);
+      nestedTypes.add(referenceTypeFromBinaryName(binaryName, definitionSupplier));
     }
     return nestedTypes.build();
   }
 
   private static List<DexType> getSealedSubClasses(
-      DexClass clazz, List<String> sealedSubclasses, AppView<?> appView) {
+      DexClass clazz, List<String> sealedSubclasses, DexDefinitionSupplier definitionSupplier) {
     ImmutableList.Builder<DexType> sealedTypes = ImmutableList.builder();
     for (String sealedSubClass : sealedSubclasses) {
       String binaryName =
           sealedSubClass.replace(
               DescriptorUtils.JAVA_PACKAGE_SEPARATOR, DescriptorUtils.INNER_CLASS_SEPARATOR);
-      DexType sealedType =
-          appView
-              .dexItemFactory()
-              .createType(DescriptorUtils.getDescriptorFromClassBinaryName(binaryName));
-      sealedTypes.add(sealedType);
+      sealedTypes.add(referenceTypeFromBinaryName(binaryName, definitionSupplier));
     }
     return sealedTypes.build();
   }
 
-  private static List<KotlinTypeInfo> getSuperTypes(List<KmType> superTypes, AppView<?> appView) {
+  private static List<KotlinTypeInfo> getSuperTypes(
+      List<KmType> superTypes, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     ImmutableList.Builder<KotlinTypeInfo> superTypeInfos = ImmutableList.builder();
     for (KmType superType : superTypes) {
-      superTypeInfos.add(KotlinTypeInfo.create(superType, appView));
+      superTypeInfos.add(KotlinTypeInfo.create(superType, definitionSupplier, reporter));
     }
     return superTypeInfos.build();
   }
 
-  private static void setCompanionObject(KmClass kmClass, DexClass hostClass, AppView<?> appView) {
+  private static void setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
     String companionObjectName = kmClass.getCompanionObject();
     if (companionObjectName == null) {
       return;
@@ -168,10 +169,8 @@
         return;
       }
     }
-    appView
-        .options()
-        .reporter
-        .warning(KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
+    reporter.warning(
+        KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
   }
 
   @Override
@@ -235,7 +234,7 @@
     }
     // Rewrite nested classes.
     for (DexType nestedClass : nestedClasses) {
-      if (appView.definitionFor(nestedClass) != null) {
+      if (appView.appInfo().isNonProgramTypeOrLiveProgramType(nestedClass)) {
         String descriptor =
             KotlinMetadataUtils.kotlinNameFromDescriptor(namingLens.lookupDescriptor(nestedClass));
         // If the class is a nested class, it should be on the form Foo.Bar$Baz, where Baz is the
@@ -246,7 +245,7 @@
     }
     // Rewrite sealed sub classes.
     for (DexType sealedSubClass : sealedSubClasses) {
-      if (appView.definitionFor(sealedSubClass) != null) {
+      if (appView.appInfo().isNonProgramTypeOrLiveProgramType(sealedSubClass)) {
         String descriptor =
             KotlinMetadataUtils.kotlinNameFromDescriptor(
                 namingLens.lookupDescriptor(sealedSubClass));
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 13ae5f5..f52f65e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -5,14 +5,15 @@
 
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
 
-import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -23,31 +24,25 @@
 public final class KotlinClassMetadataReader {
 
   public static KotlinClassLevelInfo getKotlinInfo(
-      Kotlin kotlin, DexClass clazz, AppView<?> appView) {
+      Kotlin kotlin, DexClass clazz, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     DexAnnotation meta = clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
     if (meta != null) {
       try {
-        return createKotlinInfo(kotlin, clazz, meta.annotation, appView);
+        return createKotlinInfo(kotlin, clazz, meta.annotation, definitionSupplier, reporter);
       } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
-        appView
-            .options()
-            .reporter
-            .info(
-                new StringDiagnostic(
-                    "Class "
-                        + clazz.type.toSourceString()
-                        + " has malformed kotlin.Metadata: "
-                        + e.getMessage()));
+        reporter.info(
+            new StringDiagnostic(
+                "Class "
+                    + clazz.type.toSourceString()
+                    + " has malformed kotlin.Metadata: "
+                    + e.getMessage()));
       } catch (Throwable e) {
-        appView
-            .options()
-            .reporter
-            .info(
-                new StringDiagnostic(
-                    "Unexpected error while reading "
-                        + clazz.type.toSourceString()
-                        + "'s kotlin.Metadata: "
-                        + e.getMessage()));
+        reporter.info(
+            new StringDiagnostic(
+                "Unexpected error while reading "
+                    + clazz.type.toSourceString()
+                    + "'s kotlin.Metadata: "
+                    + e.getMessage()));
       }
     }
     return NO_KOTLIN_INFO;
@@ -85,27 +80,35 @@
   }
 
   public static KotlinClassLevelInfo createKotlinInfo(
-      Kotlin kotlin, DexClass clazz, DexEncodedAnnotation metadataAnnotation, AppView<?> appView) {
+      Kotlin kotlin,
+      DexClass clazz,
+      DexEncodedAnnotation metadataAnnotation,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation);
 
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClassInfo.create(
-          ((KotlinClassMetadata.Class) kMetadata).toKmClass(), clazz, appView);
+          ((KotlinClassMetadata.Class) kMetadata).toKmClass(), clazz, definitionSupplier, reporter);
     } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
       // e.g., B.kt becomes class `BKt`
       return KotlinFileFacadeInfo.create(
-          (KotlinClassMetadata.FileFacade) kMetadata, clazz, appView);
+          (KotlinClassMetadata.FileFacade) kMetadata, clazz, definitionSupplier, reporter);
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
       // multi-file class with the same @JvmName.
       return KotlinMultiFileClassFacadeInfo.create(
-          (KotlinClassMetadata.MultiFileClassFacade) kMetadata, appView);
+          (KotlinClassMetadata.MultiFileClassFacade) kMetadata, definitionSupplier);
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
       // A single file, which is part of multi-file class.
       return KotlinMultiFileClassPartInfo.create(
-          (KotlinClassMetadata.MultiFileClassPart) kMetadata, clazz, appView);
+          (KotlinClassMetadata.MultiFileClassPart) kMetadata, clazz, definitionSupplier, reporter);
     } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
       return KotlinSyntheticClassInfo.create(
-          (KotlinClassMetadata.SyntheticClass) kMetadata, clazz, kotlin, appView);
+          (KotlinClassMetadata.SyntheticClass) kMetadata,
+          clazz,
+          kotlin,
+          definitionSupplier,
+          reporter);
     } else {
       throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
index 86db718..60851e2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -4,13 +4,17 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromDescriptor;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
 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.Reporter;
 import kotlinx.metadata.KmClassifier;
 import kotlinx.metadata.KmClassifier.TypeAlias;
 import kotlinx.metadata.KmClassifier.TypeParameter;
@@ -18,7 +22,8 @@
 
 public abstract class KotlinClassifierInfo {
 
-  public static KotlinClassifierInfo create(KmClassifier classifier, AppView<?> appView) {
+  public static KotlinClassifierInfo create(
+      KmClassifier classifier, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     if (classifier instanceof KmClassifier.Class) {
       String typeName = ((KmClassifier.Class) classifier).getName();
       // If this name starts with '.', it represents a local class or an anonymous object. This is
@@ -29,8 +34,8 @@
       }
       String descriptor = DescriptorUtils.getDescriptorFromKotlinClassifier(typeName);
       if (DescriptorUtils.isClassDescriptor(descriptor)) {
-        DexType type = appView.dexItemFactory().createType(descriptor);
-        return new KotlinClassClassifierInfo(type);
+        return new KotlinClassClassifierInfo(
+            referenceTypeFromDescriptor(descriptor, definitionSupplier));
       } else {
         return new KotlinUnknownClassClassifierInfo(typeName);
       }
@@ -39,10 +44,7 @@
     } else if (classifier instanceof KmClassifier.TypeParameter) {
       return new KotlinTypeParameterClassifierInfo(((TypeParameter) classifier).getId());
     } else {
-      appView
-          .options()
-          .reporter
-          .warning(KotlinMetadataDiagnostic.unknownClassifier(classifier.toString()));
+      reporter.warning(KotlinMetadataDiagnostic.unknownClassifier(classifier.toString()));
       return new KotlinUnknownClassifierInfo(classifier.toString());
     }
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index 3e9ee04..6f9f366 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -5,9 +5,11 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmClass;
 import kotlinx.metadata.KmConstructor;
@@ -32,11 +34,14 @@
     this.signature = signature;
   }
 
-  public static KotlinConstructorInfo create(KmConstructor kmConstructor, AppView<?> appView) {
+  public static KotlinConstructorInfo create(
+      KmConstructor kmConstructor, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     return new KotlinConstructorInfo(
         kmConstructor.getFlags(),
-        KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), appView),
-        KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), appView));
+        KotlinValueParameterInfo.create(
+            kmConstructor.getValueParameters(), definitionSupplier, reporter),
+        KotlinJvmMethodSignatureInfo.create(
+            JvmExtensionsKt.getSignature(kmConstructor), definitionSupplier));
   }
 
   public void rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index d1b2913..6dc4d4a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -8,11 +8,13 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.kotlin.KotlinMetadataUtils.KmPropertyProcessor;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -47,7 +49,8 @@
       KmDeclarationContainer container,
       Map<String, DexEncodedMethod> methodSignatureMap,
       Map<String, DexEncodedField> fieldSignatureMap,
-      AppView<?> appView) {
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
     for (KmFunction kmFunction : container.getFunctions()) {
       JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
@@ -55,7 +58,8 @@
         assert false;
         continue;
       }
-      KotlinFunctionInfo kotlinFunctionInfo = KotlinFunctionInfo.create(kmFunction, appView);
+      KotlinFunctionInfo kotlinFunctionInfo =
+          KotlinFunctionInfo.create(kmFunction, definitionSupplier, reporter);
       DexEncodedMethod method = methodSignatureMap.get(signature.asString());
       if (method == null) {
         notBackedFunctions.add(kotlinFunctionInfo);
@@ -76,7 +80,8 @@
 
     ImmutableList.Builder<KotlinPropertyInfo> notBackedProperties = ImmutableList.builder();
     for (KmProperty kmProperty : container.getProperties()) {
-      KotlinPropertyInfo kotlinPropertyInfo = KotlinPropertyInfo.create(kmProperty, appView);
+      KotlinPropertyInfo kotlinPropertyInfo =
+          KotlinPropertyInfo.create(kmProperty, definitionSupplier, reporter);
       KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty);
       boolean hasBacking = false;
       if (propertyProcessor.fieldSignature() != null) {
@@ -108,16 +113,16 @@
       }
     }
     return new KotlinDeclarationContainerInfo(
-        getTypeAliases(container.getTypeAliases(), appView),
+        getTypeAliases(container.getTypeAliases(), definitionSupplier, reporter),
         notBackedFunctions.build(),
         notBackedProperties.build());
   }
 
   private static List<KotlinTypeAliasInfo> getTypeAliases(
-      List<KmTypeAlias> aliases, AppView<?> appView) {
+      List<KmTypeAlias> aliases, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     ImmutableList.Builder<KotlinTypeAliasInfo> builder = ImmutableList.builder();
     for (KmTypeAlias alias : aliases) {
-      builder.add(KotlinTypeAliasInfo.create(alias, appView));
+      builder.add(KotlinTypeAliasInfo.create(alias, definitionSupplier, reporter));
     }
     return builder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index ee2f81d..ccd957a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -23,9 +25,12 @@
   }
 
   public static KotlinFileFacadeInfo create(
-      FileFacade kmFileFacade, DexClass clazz, AppView<?> appView) {
+      FileFacade kmFileFacade,
+      DexClass clazz,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     return new KotlinFileFacadeInfo(
-        KotlinPackageInfo.create(kmFileFacade.toKmPackage(), clazz, appView));
+        KotlinPackageInfo.create(kmFileFacade.toKmPackage(), clazz, definitionSupplier, reporter));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index c1170a1..58b07cb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -4,12 +4,15 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromBinaryName;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
 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.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmFunctionVisitor;
@@ -54,24 +57,27 @@
     this.lambdaClassOrigin = lambdaClassOrigin;
   }
 
-  static KotlinFunctionInfo create(KmFunction kmFunction, AppView<?> appView) {
+  static KotlinFunctionInfo create(
+      KmFunction kmFunction, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     return new KotlinFunctionInfo(
         kmFunction.getFlags(),
         kmFunction.getName(),
-        KotlinTypeInfo.create(kmFunction.getReturnType(), appView),
-        KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), appView),
-        KotlinValueParameterInfo.create(kmFunction.getValueParameters(), appView),
-        KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), appView),
-        KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), appView),
-        getlambdaClassOrigin(kmFunction, appView));
+        KotlinTypeInfo.create(kmFunction.getReturnType(), definitionSupplier, reporter),
+        KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), definitionSupplier, reporter),
+        KotlinValueParameterInfo.create(
+            kmFunction.getValueParameters(), definitionSupplier, reporter),
+        KotlinTypeParameterInfo.create(
+            kmFunction.getTypeParameters(), definitionSupplier, reporter),
+        KotlinJvmMethodSignatureInfo.create(
+            JvmExtensionsKt.getSignature(kmFunction), definitionSupplier),
+        getlambdaClassOrigin(kmFunction, definitionSupplier));
   }
 
-  private static DexType getlambdaClassOrigin(KmFunction kmFunction, AppView<?> appView) {
+  private static DexType getlambdaClassOrigin(
+      KmFunction kmFunction, DexDefinitionSupplier definitionSupplier) {
     String lambdaClassOriginName = JvmExtensionsKt.getLambdaClassOriginName(kmFunction);
     if (lambdaClassOriginName != null) {
-      return appView
-          .dexItemFactory()
-          .createType(DescriptorUtils.getDescriptorFromClassBinaryName(lambdaClassOriginName));
+      return referenceTypeFromBinaryName(lambdaClassOriginName, definitionSupplier);
     }
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
deleted file mode 100644
index 94f9b12..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.utils.ThreadUtils;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-
-public class KotlinInfoCollector {
-  public static void computeKotlinInfoForProgramClasses(
-      DexApplication application, AppView<?> appView, ExecutorService executorService)
-      throws ExecutionException {
-    if (appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations) {
-      return;
-    }
-    Kotlin kotlin = appView.dexItemFactory().kotlin;
-    ThreadUtils.processItems(
-        application.classes(),
-        programClass -> {
-          programClass.setKotlinInfo(kotlin.getKotlinInfo(programClass, appView));
-        },
-        executorService);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
index 0dae9cf..69c0442 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
@@ -4,9 +4,11 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromDescriptor;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toRenamedDescriptorOrDefault;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
@@ -28,12 +30,13 @@
   }
 
   public static KotlinJvmFieldSignatureInfo create(
-      JvmFieldSignature fieldSignature, AppView<?> appView) {
+      JvmFieldSignature fieldSignature, DexDefinitionSupplier definitionSupplier) {
     if (fieldSignature == null) {
       return null;
     }
     return new KotlinJvmFieldSignatureInfo(
-        fieldSignature.getName(), appView.dexItemFactory().createType(fieldSignature.getDesc()));
+        fieldSignature.getName(),
+        referenceTypeFromDescriptor(fieldSignature.getDesc(), definitionSupplier));
   }
 
   public JvmFieldSignature rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index 354364a..d9d5c9d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -4,11 +4,12 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromDescriptor;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toRenamedDescriptorOrDefault;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -36,14 +37,13 @@
   }
 
   public static KotlinJvmMethodSignatureInfo create(
-      JvmMethodSignature methodSignature, AppView<?> appView) {
+      JvmMethodSignature methodSignature, DexDefinitionSupplier definitionSupplier) {
     if (methodSignature == null) {
       return null;
     }
     String kotlinDescriptor = methodSignature.getDesc();
     String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(kotlinDescriptor);
-    DexItemFactory factory = appView.dexItemFactory();
-    DexType returnType = factory.createType(returnTypeDescriptor);
+    DexType returnType = referenceTypeFromDescriptor(returnTypeDescriptor, definitionSupplier);
     String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(kotlinDescriptor);
     if (descriptors.length == 0) {
       return new KotlinJvmMethodSignatureInfo(
@@ -51,7 +51,7 @@
     }
     ImmutableList.Builder<DexType> parameters = ImmutableList.builder();
     for (String descriptor : descriptors) {
-      parameters.add(factory.createType(descriptor));
+      parameters.add(referenceTypeFromDescriptor(descriptor, definitionSupplier));
     }
     return new KotlinJvmMethodSignatureInfo(
         methodSignature.getName(), returnType, parameters.build());
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 107d197..d25f105 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -8,9 +8,11 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmLambda;
 import kotlinx.metadata.KmLambdaVisitor;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
@@ -25,7 +27,11 @@
     this.function = function;
   }
 
-  static KotlinLambdaInfo create(DexClass clazz, KmLambda lambda, AppView<?> appView) {
+  static KotlinLambdaInfo create(
+      DexClass clazz,
+      KmLambda lambda,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     if (lambda == null) {
       assert false;
       return null;
@@ -37,7 +43,8 @@
     }
     for (DexEncodedMethod method : clazz.methods()) {
       if (toJvmMethodSignature(method.method).asString().equals(signature.asString())) {
-        KotlinFunctionInfo kotlinFunctionInfo = KotlinFunctionInfo.create(lambda.function, appView);
+        KotlinFunctionInfo kotlinFunctionInfo =
+            KotlinFunctionInfo.create(lambda.function, definitionSupplier, reporter);
         method.setKotlinMemberInfo(kotlinFunctionInfo);
         return new KotlinLambdaInfo(kotlinFunctionInfo);
       }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
new file mode 100644
index 0000000..d280d134
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
+
+public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
+
+  private final AppView<?> appView;
+
+  public KotlinMetadataEnqueuerExtension(AppView<?> appView) {
+    this.appView = appView;
+  }
+
+  @Override
+  public void processNewlyLiveClass(
+      DexProgramClass clazz, EnqueuerWorklist worklist, DexDefinitionSupplier definitionSupplier) {
+    Kotlin kotlin = appView.dexItemFactory().kotlin;
+    clazz.setKotlinInfo(
+        KotlinClassMetadataReader.getKotlinInfo(
+            kotlin, clazz, definitionSupplier, appView.options().reporter));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index b3e0eb4..b3c167f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -67,8 +67,6 @@
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
-    // TODO(b/152283077): Don't disable the assert.
-    appView.appInfo().disableDefinitionForAssert();
     ThreadUtils.processItems(
         appView.appInfo().classes(),
         clazz -> {
@@ -95,7 +93,6 @@
           }
         },
         executorService);
-    appView.appInfo().enableDefinitionForAssert();
   }
 
   private DexAnnotation createKotlinMetadataAnnotation(KotlinClassHeader header) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 277201c..fb173a2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
@@ -132,4 +133,20 @@
   static String kotlinNameFromDescriptor(DexString descriptor) {
     return DescriptorUtils.getBinaryNameFromDescriptor(descriptor.toString());
   }
+
+  static DexType referenceTypeFromBinaryName(
+      String binaryName, DexDefinitionSupplier definitionSupplier) {
+    return referenceTypeFromDescriptor(
+        DescriptorUtils.getDescriptorFromClassBinaryName(binaryName), definitionSupplier);
+  }
+
+  static DexType referenceTypeFromDescriptor(
+      String descriptor, DexDefinitionSupplier definitionSupplier) {
+    DexType type = definitionSupplier.dexItemFactory().createType(descriptor);
+    // Lookup the definition, ignoring the result. This populates the sets in the Enqueuer.
+    if (type.isClassType()) {
+      definitionSupplier.definitionFor(type);
+    }
+    return type;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
index 02efea2..7eaa6b8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
@@ -28,7 +29,7 @@
   }
 
   static KotlinMultiFileClassFacadeInfo create(
-      MultiFileClassFacade kmMultiFileClassFacade, AppView<?> appView) {
+      MultiFileClassFacade kmMultiFileClassFacade, DexDefinitionSupplier appView) {
     ImmutableList.Builder<DexType> builder = ImmutableList.builder();
     for (String partClassName : kmMultiFileClassFacade.getPartClassNames()) {
       String descriptor = DescriptorUtils.getDescriptorFromClassBinaryName(partClassName);
@@ -55,7 +56,7 @@
         new KotlinClassMetadata.MultiFileClassFacade.Writer();
     List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
     for (DexType partClassName : partClassNames) {
-      if (appView.definitionFor(partClassName) != null) {
+      if (appView.appInfo().isNonProgramTypeOrLiveProgramType(partClassName)) {
         DexString descriptor = namingLens.lookupDescriptor(partClassName);
         String classifier = DescriptorUtils.descriptorToKotlinClassifier(descriptor.toString());
         partClassNameStrings.add(classifier);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 69ea8e4..6a06b7d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -25,10 +27,13 @@
   }
 
   static KotlinMultiFileClassPartInfo create(
-      MultiFileClassPart classPart, DexClass clazz, AppView<?> appView) {
+      MultiFileClassPart classPart,
+      DexClass clazz,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     return new KotlinMultiFileClassPartInfo(
         classPart.getFacadeClassName(),
-        KotlinPackageInfo.create(classPart.toKmPackage(), clazz, appView));
+        KotlinPackageInfo.create(classPart.toKmPackage(), clazz, definitionSupplier, reporter));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index c90d673..7a667e6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -9,10 +9,12 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import java.util.HashMap;
 import java.util.Map;
 import kotlinx.metadata.KmPackage;
@@ -29,7 +31,11 @@
     this.containerInfo = containerInfo;
   }
 
-  public static KotlinPackageInfo create(KmPackage kmPackage, DexClass clazz, AppView<?> appView) {
+  public static KotlinPackageInfo create(
+      KmPackage kmPackage,
+      DexClass clazz,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : clazz.fields()) {
       fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -40,7 +46,8 @@
     }
     return new KotlinPackageInfo(
         JvmExtensionsKt.getModuleName(kmPackage),
-        KotlinDeclarationContainerInfo.create(kmPackage, methodMap, fieldMap, appView));
+        KotlinDeclarationContainerInfo.create(
+            kmPackage, methodMap, fieldMap, definitionSupplier, reporter));
   }
 
   public void rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index a94b6fc..802428a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -5,10 +5,12 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmPropertyVisitor;
@@ -19,35 +21,35 @@
 public class KotlinPropertyInfo implements KotlinFieldLevelInfo, KotlinMethodLevelInfo {
 
   // Original flags.
-  final int flags;
+  private final int flags;
 
   // Original getter flags. E.g., for property getter.
-  final int getterFlags;
+  private final int getterFlags;
 
   // Original setter flags. E.g., for property setter.
-  final int setterFlags;
+  private final int setterFlags;
 
   // Original property name for (extension) property. Otherwise, null.
-  final String name;
+  private final String name;
 
   // Original return type information. This should never be NULL (even for setters without field).
-  final KotlinTypeInfo returnType;
+  private final KotlinTypeInfo returnType;
 
-  final KotlinTypeInfo receiverParameterType;
+  private final KotlinTypeInfo receiverParameterType;
 
-  final KotlinValueParameterInfo setterParameter;
+  private final KotlinValueParameterInfo setterParameter;
 
-  final List<KotlinTypeParameterInfo> typeParameters;
+  private final List<KotlinTypeParameterInfo> typeParameters;
 
-  final int jvmFlags;
+  private final int jvmFlags;
 
-  final KotlinJvmFieldSignatureInfo fieldSignature;
+  private final KotlinJvmFieldSignatureInfo fieldSignature;
 
-  final KotlinJvmMethodSignatureInfo getterSignature;
+  private final KotlinJvmMethodSignatureInfo getterSignature;
 
-  final KotlinJvmMethodSignatureInfo setterSignature;
+  private final KotlinJvmMethodSignatureInfo setterSignature;
 
-  final KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations;
+  private final KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations;
 
   private KotlinPropertyInfo(
       int flags,
@@ -78,24 +80,28 @@
     this.syntheticMethodForAnnotations = syntheticMethodForAnnotations;
   }
 
-  public static KotlinPropertyInfo create(KmProperty kmProperty, AppView<?> appView) {
+  public static KotlinPropertyInfo create(
+      KmProperty kmProperty, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     return new KotlinPropertyInfo(
         kmProperty.getFlags(),
         kmProperty.getGetterFlags(),
         kmProperty.getSetterFlags(),
         kmProperty.getName(),
-        KotlinTypeInfo.create(kmProperty.getReturnType(), appView),
-        KotlinTypeInfo.create(kmProperty.getReceiverParameterType(), appView),
-        KotlinValueParameterInfo.create(kmProperty.getSetterParameter(), appView),
-        KotlinTypeParameterInfo.create(kmProperty.getTypeParameters(), appView),
+        KotlinTypeInfo.create(kmProperty.getReturnType(), definitionSupplier, reporter),
+        KotlinTypeInfo.create(kmProperty.getReceiverParameterType(), definitionSupplier, reporter),
+        KotlinValueParameterInfo.create(
+            kmProperty.getSetterParameter(), definitionSupplier, reporter),
+        KotlinTypeParameterInfo.create(
+            kmProperty.getTypeParameters(), definitionSupplier, reporter),
         JvmExtensionsKt.getJvmFlags(kmProperty),
-        KotlinJvmFieldSignatureInfo.create(JvmExtensionsKt.getFieldSignature(kmProperty), appView),
+        KotlinJvmFieldSignatureInfo.create(
+            JvmExtensionsKt.getFieldSignature(kmProperty), definitionSupplier),
         KotlinJvmMethodSignatureInfo.create(
-            JvmExtensionsKt.getGetterSignature(kmProperty), appView),
+            JvmExtensionsKt.getGetterSignature(kmProperty), definitionSupplier),
         KotlinJvmMethodSignatureInfo.create(
-            JvmExtensionsKt.getSetterSignature(kmProperty), appView),
+            JvmExtensionsKt.getSetterSignature(kmProperty), definitionSupplier),
         KotlinJvmMethodSignatureInfo.create(
-            JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), appView));
+            JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), definitionSupplier));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 8dfe761..cc11647 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.InconsistentKotlinMetadataException;
 import kotlinx.metadata.KmLambda;
 import kotlinx.metadata.jvm.KotlinClassHeader;
@@ -34,7 +36,11 @@
   }
 
   static KotlinSyntheticClassInfo create(
-      SyntheticClass syntheticClass, DexClass clazz, Kotlin kotlin, AppView<?> appView) {
+      SyntheticClass syntheticClass,
+      DexClass clazz,
+      Kotlin kotlin,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     KmLambda lambda = null;
     if (syntheticClass.isLambda()) {
       try {
@@ -45,7 +51,9 @@
       }
     }
     return new KotlinSyntheticClassInfo(
-        lambda != null ? KotlinLambdaInfo.create(clazz, lambda, appView) : null,
+        lambda != null
+            ? KotlinLambdaInfo.create(clazz, lambda, definitionSupplier, reporter)
+            : null,
         getFlavour(syntheticClass, clazz, kotlin));
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index 974469c..b4b5354 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmTypeAlias;
 import kotlinx.metadata.KmTypeAliasVisitor;
@@ -38,14 +40,15 @@
     this.annotations = annotations;
   }
 
-  public static KotlinTypeAliasInfo create(KmTypeAlias alias, AppView<?> appView) {
+  public static KotlinTypeAliasInfo create(
+      KmTypeAlias alias, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     return new KotlinTypeAliasInfo(
         alias.getFlags(),
         alias.getName(),
-        KotlinTypeInfo.create(alias.underlyingType, appView),
-        KotlinTypeInfo.create(alias.expandedType, appView),
-        KotlinTypeParameterInfo.create(alias.getTypeParameters(), appView),
-        KotlinAnnotationInfo.create(alias.getAnnotations(), appView));
+        KotlinTypeInfo.create(alias.underlyingType, definitionSupplier, reporter),
+        KotlinTypeInfo.create(alias.expandedType, definitionSupplier, reporter),
+        KotlinTypeParameterInfo.create(alias.getTypeParameters(), definitionSupplier, reporter),
+        KotlinAnnotationInfo.create(alias.getAnnotations(), definitionSupplier));
   }
 
   void rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index f00a544..204bcbb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import kotlinx.metadata.KmType;
@@ -47,27 +49,30 @@
     return arguments;
   }
 
-  static KotlinTypeInfo create(KmType kmType, AppView<?> appView) {
+  static KotlinTypeInfo create(
+      KmType kmType, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     if (kmType == null) {
       return null;
     }
     return new KotlinTypeInfo(
         kmType.getFlags(),
-        KotlinClassifierInfo.create(kmType.classifier, appView),
-        KotlinTypeInfo.create(kmType.getAbbreviatedType(), appView),
-        KotlinTypeInfo.create(kmType.getOuterType(), appView),
-        getArguments(kmType.getArguments(), appView),
-        KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmType), appView));
+        KotlinClassifierInfo.create(kmType.classifier, definitionSupplier, reporter),
+        KotlinTypeInfo.create(kmType.getAbbreviatedType(), definitionSupplier, reporter),
+        KotlinTypeInfo.create(kmType.getOuterType(), definitionSupplier, reporter),
+        getArguments(kmType.getArguments(), definitionSupplier, reporter),
+        KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmType), definitionSupplier));
   }
 
   private static List<KotlinTypeProjectionInfo> getArguments(
-      List<KmTypeProjection> projections, AppView<?> appView) {
+      List<KmTypeProjection> projections,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     if (projections.isEmpty()) {
       return EMPTY_ARGUMENTS;
     }
     ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = ImmutableList.builder();
     for (KmTypeProjection projection : projections) {
-      arguments.add(KotlinTypeProjectionInfo.create(projection, appView));
+      arguments.add(KotlinTypeProjectionInfo.create(projection, definitionSupplier, reporter));
     }
     return arguments.build();
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index e79ea83..fd11c11 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import kotlinx.metadata.KmType;
@@ -45,35 +47,41 @@
   }
 
   private static KotlinTypeParameterInfo create(
-      KmTypeParameter kmTypeParameter, AppView<?> appView) {
+      KmTypeParameter kmTypeParameter,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     return new KotlinTypeParameterInfo(
         kmTypeParameter.getFlags(),
         kmTypeParameter.getId(),
         kmTypeParameter.getName(),
         kmTypeParameter.getVariance(),
-        getUpperBounds(kmTypeParameter.getUpperBounds(), appView),
-        KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmTypeParameter), appView));
+        getUpperBounds(kmTypeParameter.getUpperBounds(), definitionSupplier, reporter),
+        KotlinAnnotationInfo.create(
+            JvmExtensionsKt.getAnnotations(kmTypeParameter), definitionSupplier));
   }
 
   static List<KotlinTypeParameterInfo> create(
-      List<KmTypeParameter> kmTypeParameters, AppView<?> appView) {
+      List<KmTypeParameter> kmTypeParameters,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     if (kmTypeParameters.isEmpty()) {
       return EMPTY_TYPE_PARAMETERS;
     }
     ImmutableList.Builder<KotlinTypeParameterInfo> builder = ImmutableList.builder();
     for (KmTypeParameter kmTypeParameter : kmTypeParameters) {
-      builder.add(create(kmTypeParameter, appView));
+      builder.add(create(kmTypeParameter, definitionSupplier, reporter));
     }
     return builder.build();
   }
 
-  private static List<KotlinTypeInfo> getUpperBounds(List<KmType> upperBounds, AppView<?> appView) {
+  private static List<KotlinTypeInfo> getUpperBounds(
+      List<KmType> upperBounds, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
     if (upperBounds.isEmpty()) {
       return EMPTY_UPPER_BOUNDS;
     }
     ImmutableList.Builder<KotlinTypeInfo> builder = ImmutableList.builder();
     for (KmType upperBound : upperBounds) {
-      builder.add(KotlinTypeInfo.create(upperBound, appView));
+      builder.add(KotlinTypeInfo.create(upperBound, definitionSupplier, reporter));
     }
     return builder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
index 9ab8adc..68da5db 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmTypeProjection;
 import kotlinx.metadata.KmVariance;
 
@@ -21,9 +23,13 @@
     this.typeInfo = typeInfo;
   }
 
-  static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection, AppView<?> appView) {
+  static KotlinTypeProjectionInfo create(
+      KmTypeProjection kmTypeProjection,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     return new KotlinTypeProjectionInfo(
-        kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType(), appView));
+        kmTypeProjection.getVariance(),
+        KotlinTypeInfo.create(kmTypeProjection.getType(), definitionSupplier, reporter));
   }
 
   private boolean isStarProjection() {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index d092495..ee2f99b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import kotlinx.metadata.KmType;
@@ -33,7 +35,10 @@
     this.varargElementType = varargElementType;
   }
 
-  static KotlinValueParameterInfo create(KmValueParameter kmValueParameter, AppView<?> appView) {
+  static KotlinValueParameterInfo create(
+      KmValueParameter kmValueParameter,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     if (kmValueParameter == null) {
       return null;
     }
@@ -41,18 +46,21 @@
     return new KotlinValueParameterInfo(
         kmValueParameter.getFlags(),
         kmValueParameter.getName(),
-        KotlinTypeInfo.create(kmType, appView),
-        KotlinTypeInfo.create(kmValueParameter.getVarargElementType(), appView));
+        KotlinTypeInfo.create(kmType, definitionSupplier, reporter),
+        KotlinTypeInfo.create(
+            kmValueParameter.getVarargElementType(), definitionSupplier, reporter));
   }
 
   static List<KotlinValueParameterInfo> create(
-      List<KmValueParameter> parameters, AppView<?> appView) {
+      List<KmValueParameter> parameters,
+      DexDefinitionSupplier definitionSupplier,
+      Reporter reporter) {
     if (parameters.isEmpty()) {
       return EMPTY_VALUE_PARAMETERS;
     }
     ImmutableList.Builder<KotlinValueParameterInfo> builder = ImmutableList.builder();
     for (KmValueParameter parameter : parameters) {
-      builder.add(create(parameter, appView));
+      builder.add(create(parameter, definitionSupplier, reporter));
     }
     return builder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index c57a91a..8cffa05 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -271,7 +271,7 @@
     if (holder == null || holder.isNotProgramClass()) {
       return;
     }
-    definition = appView.appInfo().resolveField(field);
+    definition = appView.appInfo().resolveField(field).getResolvedField();
     if (definition == null) {
       // The program is already broken in the sense that it has an unresolvable field reference.
       // Leave it as-is.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index 6652d4a..f8d80cb 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.naming;
 
-
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -22,17 +22,20 @@
   private final MemberNamingStrategy strategy;
   private final BiPredicate<DexString, DexField> isAvailable;
 
-  public FieldNamingState(AppView<?> appView, MemberNamingStrategy strategy) {
+  public FieldNamingState(
+      AppView<? extends AppInfoWithClassHierarchy> appView, MemberNamingStrategy strategy) {
     this(appView, strategy, new ReservedFieldNamingState(appView));
   }
 
   public FieldNamingState(
-      AppView<?> appView, MemberNamingStrategy strategy, ReservedFieldNamingState reservedNames) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      MemberNamingStrategy strategy,
+      ReservedFieldNamingState reservedNames) {
     this(appView, strategy, reservedNames, new IdentityHashMap<>());
   }
 
   private FieldNamingState(
-      AppView<?> appView,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       MemberNamingStrategy strategy,
       ReservedFieldNamingState reservedNames,
       Map<DexType, InternalState> internalStates) {
@@ -50,7 +53,7 @@
   }
 
   public DexString getOrCreateNameFor(DexField field) {
-    DexEncodedField encodedField = appView.appInfo().resolveField(field);
+    DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
     if (encodedField != null) {
       DexClass clazz = appView.definitionFor(encodedField.holder());
       if (clazz == null) {
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java b/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
index 8fbcfcd..693f373 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
@@ -12,10 +13,11 @@
 
 abstract class FieldNamingStateBase<T> {
 
-  final AppView<?> appView;
+  final AppView<? extends AppInfoWithClassHierarchy> appView;
   final Map<DexType, T> internalStates;
 
-  FieldNamingStateBase(AppView<?> appView, Map<DexType, T> internalStates) {
+  FieldNamingStateBase(
+      AppView<? extends AppInfoWithClassHierarchy> appView, Map<DexType, T> internalStates) {
     this.appView = appView;
     this.internalStates = internalStates;
   }
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index f789fe4..d133c3f 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.naming;
 
-
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -32,12 +32,12 @@
 
 class MinifiedRenaming extends NamingLens {
 
-  final AppView<?> appView;
+  final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final Map<String, String> packageRenaming;
   private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
 
   MinifiedRenaming(
-      AppView<?> appView,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       ClassRenaming classRenaming,
       MethodRenaming methodRenaming,
       FieldRenaming fieldRenaming) {
@@ -99,7 +99,7 @@
       return renamed;
     }
     // If the method does not have a direct renaming, return the resolutions mapping.
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
     if (resolutionResult.isSingleResolution()) {
       return renaming.getOrDefault(resolutionResult.getSingleTarget().method, method.name);
     }
@@ -166,7 +166,7 @@
       return true;
     }
 
-    ResolutionResult resolution = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolution = appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
     assert resolution != null;
 
     if (resolution.isSingleResolution()) {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 5e23d98..ab735ca 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
@@ -583,7 +584,7 @@
     private final Map<DexString, DexType> classRenamingsMappingToDifferentName;
 
     ProguardMapMinifiedRenaming(
-        AppView<?> appView,
+        AppView<? extends AppInfoWithClassHierarchy> appView,
         ClassRenaming classRenaming,
         MethodRenaming methodRenaming,
         FieldRenaming fieldRenaming,
diff --git a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
index 9a0051b..4e3e98c 100644
--- a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -13,7 +14,7 @@
 
 class ReservedFieldNamingState extends FieldNamingStateBase<InternalState> {
 
-  ReservedFieldNamingState(AppView<?> appView) {
+  ReservedFieldNamingState(AppView<? extends AppInfoWithClassHierarchy> appView) {
     super(appView, new IdentityHashMap<>());
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
index 8e5c3c3..1cdeec3 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
@@ -157,7 +157,7 @@
       DexEncodedMethod definition = subclass.lookupVirtualMethod(method);
       if (definition == null) {
         DexEncodedMethod resolutionTarget =
-            appView.appInfo().resolveMethodOnClass(subclass, method).getSingleTarget();
+            appView.appInfo().resolveMethodOnClass(method, subclass).getSingleTarget();
         if (resolutionTarget == null || resolutionTarget.isAbstract()) {
           // The fact that this class does not declare the bridge (or the bridge is abstract) should
           // not prevent us from hoisting the bridge.
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 8315925..5da8e5f 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -6,11 +6,13 @@
 import static com.android.tools.r8.dex.Constants.ACC_PRIVATE;
 import static com.android.tools.r8.dex.Constants.ACC_PROTECTED;
 import static com.android.tools.r8.dex.Constants.ACC_PUBLIC;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.InnerClassAttribute;
@@ -82,43 +84,69 @@
   }
 
   private void publicizeType(DexType type) {
-    DexClass clazz = application.definitionFor(type);
-    if (clazz != null && clazz.isProgramClass()) {
-      clazz.accessFlags.promoteToPublic();
-
-      // Publicize fields.
-      clazz.forEachField(field -> field.accessFlags.promoteToPublic());
-
-      // Publicize methods.
-      Set<DexEncodedMethod> privateInstanceEncodedMethods = new LinkedHashSet<>();
-      clazz.forEachMethod(encodedMethod -> {
-        if (publicizeMethod(clazz, encodedMethod)) {
-          privateInstanceEncodedMethods.add(encodedMethod);
-        }
-      });
-      if (!privateInstanceEncodedMethods.isEmpty()) {
-        clazz.virtualizeMethods(privateInstanceEncodedMethods);
-      }
-
-      // Publicize inner class attribute.
-      InnerClassAttribute attr = clazz.getInnerClassAttributeForThisClass();
-      if (attr != null) {
-        int accessFlags = ((attr.getAccess() | ACC_PUBLIC) & ~ACC_PRIVATE) & ~ACC_PROTECTED;
-        clazz.replaceInnerClassAttributeForThisClass(
-            new InnerClassAttribute(
-                accessFlags, attr.getInner(), attr.getOuter(), attr.getInnerName()));
-      }
+    DexProgramClass clazz = asProgramClassOrNull(application.definitionFor(type));
+    if (clazz != null) {
+      publicizeClass(clazz);
     }
-
     subtypingInfo.forAllImmediateExtendsSubtypes(type, this::publicizeType);
   }
 
-  private boolean publicizeMethod(DexClass holder, DexEncodedMethod encodedMethod) {
-    MethodAccessFlags accessFlags = encodedMethod.accessFlags;
+  private void publicizeClass(DexProgramClass clazz) {
+    clazz.accessFlags.promoteToPublic();
+
+    // Publicize fields.
+    clazz.forEachField(
+        field -> {
+          if (field.isPublic()) {
+            return;
+          }
+          if (appView.appInfo().isPinned(field.field)) {
+            // TODO(b/131130038): Also do not publicize package-private and protected fields that
+            //  are kept.
+            if (field.isPrivate()) {
+              return;
+            }
+          }
+          field.accessFlags.promoteToPublic();
+        });
+
+    // Publicize methods.
+    Set<DexEncodedMethod> privateInstanceMethods = new LinkedHashSet<>();
+    clazz.forEachMethod(
+        method -> {
+          if (publicizeMethod(clazz, method)) {
+            privateInstanceMethods.add(method);
+          }
+        });
+    if (!privateInstanceMethods.isEmpty()) {
+      clazz.virtualizeMethods(privateInstanceMethods);
+    }
+
+    // Publicize inner class attribute.
+    InnerClassAttribute attr = clazz.getInnerClassAttributeForThisClass();
+    if (attr != null) {
+      int accessFlags = ((attr.getAccess() | ACC_PUBLIC) & ~ACC_PRIVATE) & ~ACC_PROTECTED;
+      clazz.replaceInnerClassAttributeForThisClass(
+          new InnerClassAttribute(
+              accessFlags, attr.getInner(), attr.getOuter(), attr.getInnerName()));
+    }
+  }
+
+  private boolean publicizeMethod(DexProgramClass holder, DexEncodedMethod method) {
+    MethodAccessFlags accessFlags = method.accessFlags;
     if (accessFlags.isPublic()) {
       return false;
     }
-    if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(encodedMethod.method)) {
+    // If this method is mentioned in keep rules, do not transform (rule applications changed).
+    if (appView.appInfo().isPinned(method.method)) {
+      // TODO(b/131130038): Also do not publicize package-private and protected methods that are
+      //  kept.
+      if (method.isPrivate()) {
+        return false;
+      }
+    }
+
+    if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(method.method)) {
       // TODO(b/150589374): This should check for dispatch targets or just abandon in
       //  package-private.
       accessFlags.promoteToPublic();
@@ -126,10 +154,6 @@
     }
 
     if (!accessFlags.isStatic()) {
-      // If this method is mentioned in keep rules, do not transform (rule applications changed).
-      if (appView.appInfo().isPinned(encodedMethod.method)) {
-        return false;
-      }
 
       // We can't publicize private instance methods in interfaces or methods that are copied from
       // interfaces to lambda-desugared classes because this will be added as a new default method.
@@ -138,20 +162,20 @@
         return false;
       }
 
-      boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, encodedMethod.method);
+      boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, method.method);
       if (wasSeen) {
         // We can't do anything further because even renaming is not allowed due to the keep rule.
-        if (appView.rootSet().mayNotBeMinified(encodedMethod.method, appView)) {
+        if (appView.rootSet().mayNotBeMinified(method.method, appView)) {
           return false;
         }
         // TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
         return false;
       }
-      lenseBuilder.add(encodedMethod.method);
+      lenseBuilder.add(method.method);
       accessFlags.promoteToFinal();
       accessFlags.promoteToPublic();
       // The method just became public and is therefore not a library override.
-      encodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
+      method.setLibraryMethodOverride(OptionalBool.FALSE);
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 42e03e0..a3e4666 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -15,12 +15,13 @@
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
 import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 
@@ -114,7 +115,7 @@
   }
 
   private DexEncodedMethod classLookup(DexMethod method) {
-    return appView.appInfo().resolveMethodOnClass(method.holder, method).getSingleTarget();
+    return appView.appInfo().resolveMethodOnClass(method, method.holder).getSingleTarget();
   }
 
   private DexEncodedMethod interfaceLookup(DexMethod method) {
@@ -122,11 +123,11 @@
   }
 
   private DexEncodedMethod anyLookup(DexMethod method) {
-    return appView.appInfo().resolveMethod(method.holder, method).getSingleTarget();
+    return appView.appInfo().unsafeResolveMethodDueToDexFormat(method).getSingleTarget();
   }
 
   private void computeMethodRebinding(
-      Map<DexMethod, Set<DexEncodedMethod>> methodsWithContexts,
+      Map<DexMethod, ProgramMethodSet> methodsWithContexts,
       Function<DexMethod, DexEncodedMethod> lookupTarget,
       Type invokeType) {
     for (DexMethod method : methodsWithContexts.keySet()) {
@@ -158,9 +159,9 @@
           // If the target class is not public but the targeted method is, we might run into
           // visibility problems when rebinding.
           final DexEncodedMethod finalTarget = target;
-          Set<DexEncodedMethod> contexts = methodsWithContexts.get(method);
+          ProgramMethodSet contexts = methodsWithContexts.get(method);
           if (contexts.stream()
-              .anyMatch(context -> mayNeedBridgeForVisibility(context.holder(), finalTarget))) {
+              .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
             target =
                 insertBridgeForVisibilityIfNeeded(
                     method, target, originalClass, targetClass, lookupTarget);
@@ -216,16 +217,18 @@
     return findHolderForInterfaceMethodBridge(superClass.asProgramClass(), iface);
   }
 
-  private boolean mayNeedBridgeForVisibility(DexType context, DexEncodedMethod method) {
+  private boolean mayNeedBridgeForVisibility(ProgramMethod context, DexEncodedMethod method) {
     DexType holderType = method.holder();
     DexClass holder = appView.definitionFor(holderType);
     if (holder == null) {
       return false;
     }
     ConstraintWithTarget classVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holderType, holder.accessFlags, appView);
+        ConstraintWithTarget.deriveConstraint(
+            context.getHolder(), holderType, holder.accessFlags, appView);
     ConstraintWithTarget methodVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holderType, method.accessFlags, appView);
+        ConstraintWithTarget.deriveConstraint(
+            context.getHolder(), holderType, method.accessFlags, appView);
     // We may need bridge for visibility if the target class is not visible while the target method
     // is visible from the calling context.
     return classVisibility == ConstraintWithTarget.NEVER
@@ -297,8 +300,8 @@
   }
 
   private void computeFieldRebindingForIndirectAccessWithContexts(
-      DexField field, Set<DexEncodedMethod> contexts) {
-    DexEncodedField target = appView.appInfo().resolveField(field);
+      DexField field, ProgramMethodSet contexts) {
+    DexEncodedField target = appView.appInfo().resolveField(field).getResolvedField();
     if (target == null) {
       assert false;
       return;
@@ -315,14 +318,14 @@
         .allMatch(
             context ->
                 isMemberVisibleFromOriginalContext(
-                    appView, context.holder(), target.holder(), target.accessFlags))) {
+                    appView, context, target.holder(), target.accessFlags))) {
       builder.map(
           field, lense.lookupField(validTargetFor(target.field, field, DexClass::lookupField)));
     }
   }
 
   public static boolean isTypeVisibleFromContext(
-      AppView<?> appView, DexType context, DexType type) {
+      AppView<?> appView, ProgramMethod context, DexType type) {
     DexType baseType = type.toBaseType(appView.dexItemFactory());
     if (baseType.isPrimitiveType()) {
       return true;
@@ -331,26 +334,28 @@
   }
 
   public static boolean isClassTypeVisibleFromContext(
-      AppView<?> appView, DexType context, DexType type) {
+      AppView<?> appView, ProgramMethod context, DexType type) {
     assert type.isClassType();
     DexClass clazz = appView.definitionFor(type);
     return clazz != null && isClassTypeVisibleFromContext(appView, context, clazz);
   }
 
   public static boolean isClassTypeVisibleFromContext(
-      AppView<?> appView, DexType context, DexClass clazz) {
+      AppView<?> appView, ProgramMethod context, DexClass clazz) {
     ConstraintWithTarget classVisibility =
-        ConstraintWithTarget.deriveConstraint(context, clazz.type, clazz.accessFlags, appView);
+        ConstraintWithTarget.deriveConstraint(
+            context.getHolder(), clazz.type, clazz.accessFlags, appView);
     return classVisibility != ConstraintWithTarget.NEVER;
   }
 
   public static boolean isMemberVisibleFromOriginalContext(
-      AppView<?> appView, DexType context, DexType holder, AccessFlags<?> memberAccessFlags) {
+      AppView<?> appView, ProgramMethod context, DexType holder, AccessFlags<?> memberAccessFlags) {
     if (!isClassTypeVisibleFromContext(appView, context, holder)) {
       return false;
     }
     ConstraintWithTarget memberVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holder, memberAccessFlags, appView);
+        ConstraintWithTarget.deriveConstraint(
+            context.getHolder(), holder, memberAccessFlags, appView);
     return memberVisibility != ConstraintWithTarget.NEVER;
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 45892a2..96dd07b 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -83,7 +83,7 @@
       if (kind == InvokeKind.SUPER) {
         // This is a visibility forward, so check for the direct target.
         DexEncodedMethod targetMethod =
-            appView.appInfo().resolveMethod(target.holder, target).getSingleTarget();
+            appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getSingleTarget();
         if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
           if (Log.ENABLED) {
             Log.info(
diff --git a/src/main/java/com/android/tools/r8/relocator/Relocator.java b/src/main/java/com/android/tools/r8/relocator/Relocator.java
index a4caad6..aaf83b1 100644
--- a/src/main/java/com/android/tools/r8/relocator/Relocator.java
+++ b/src/main/java/com/android/tools/r8/relocator/Relocator.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
@@ -81,7 +80,7 @@
     try {
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
 
-      AppInfo appInfo = new AppInfoWithClassHierarchy(app);
+      AppInfo appInfo = new AppInfo(app);
       AppView<?> appView = AppView.createForRelocator(appInfo, options);
       appView.setAppServices(AppServices.builder(appView).build());
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 77edcc6..308cf69 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.utils.PredicateSet;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.Visibility;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.ImmutableSortedSet.Builder;
@@ -121,15 +122,15 @@
   /** Information about instantiated classes and their allocation sites. */
   private final ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection;
   /** Set of all methods referenced in virtual invokes, along with calling context. */
-  public final SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes;
+  public final SortedMap<DexMethod, ProgramMethodSet> virtualInvokes;
   /** Set of all methods referenced in interface invokes, along with calling context. */
-  public final SortedMap<DexMethod, Set<DexEncodedMethod>> interfaceInvokes;
+  public final SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes;
   /** Set of all methods referenced in super invokes, along with calling context. */
-  public final SortedMap<DexMethod, Set<DexEncodedMethod>> superInvokes;
+  public final SortedMap<DexMethod, ProgramMethodSet> superInvokes;
   /** Set of all methods referenced in direct invokes, along with calling context. */
-  public final SortedMap<DexMethod, Set<DexEncodedMethod>> directInvokes;
+  public final SortedMap<DexMethod, ProgramMethodSet> directInvokes;
   /** Set of all methods referenced in static invokes, along with calling context. */
-  public final SortedMap<DexMethod, Set<DexEncodedMethod>> staticInvokes;
+  public final SortedMap<DexMethod, ProgramMethodSet> staticInvokes;
   /**
    * Set of live call sites in the code. Note that if desugaring has taken place call site objects
    * will have been removed from the code.
@@ -208,11 +209,11 @@
       SortedSet<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
       ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> interfaceInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> superInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> directInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> staticInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> superInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> directInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
       Set<DexCallSite> callSites,
       Set<DexReference> pinnedItems,
       Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
@@ -280,7 +281,7 @@
   }
 
   public AppInfoWithLiveness(
-      AppInfoWithClassHierarchy appInfoWithSubtyping,
+      AppInfoWithClassHierarchy appInfoWithClassHierarchy,
       Set<DexType> deadProtoTypes,
       Set<DexType> missingTypes,
       Set<DexType> liveTypes,
@@ -293,11 +294,11 @@
       SortedSet<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
       ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> interfaceInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> superInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> directInvokes,
-      SortedMap<DexMethod, Set<DexEncodedMethod>> staticInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> superInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> directInvokes,
+      SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
       Set<DexCallSite> callSites,
       Set<DexReference> pinnedItems,
       Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
@@ -321,7 +322,7 @@
       EnumValueInfoMapCollection enumValueInfoMaps,
       Set<DexType> constClassReferences,
       Map<DexType, Visibility> initClassReferences) {
-    super(appInfoWithSubtyping);
+    super(appInfoWithClassHierarchy);
     this.deadProtoTypes = deadProtoTypes;
     this.missingTypes = missingTypes;
     this.liveTypes = liveTypes;
@@ -853,7 +854,8 @@
     }
     DexType holder = field.holder();
     return fieldAccessInfo.isWrittenOnlyInMethodSatisfying(
-        method -> method.isInstanceInitializer() && method.holder() == holder);
+        method ->
+            method.getDefinition().isInstanceInitializer() && method.getHolderType() == holder);
   }
 
   public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
@@ -885,15 +887,15 @@
     return builder.build();
   }
 
-  private static <T extends PresortedComparable<T>, S>
-      SortedMap<T, Set<S>> rewriteKeysConservativelyWhileMergingValues(
-          Map<T, Set<S>> original, Function<T, Set<T>> rewrite) {
-    SortedMap<T, Set<S>> result = new TreeMap<>(PresortedComparable::slowCompare);
+  private static <T extends PresortedComparable<T>>
+      SortedMap<T, ProgramMethodSet> rewriteKeysConservativelyWhileMergingValues(
+          Map<T, ProgramMethodSet> original, Function<T, Set<T>> rewrite) {
+    SortedMap<T, ProgramMethodSet> result = new TreeMap<>(PresortedComparable::slowCompare);
     for (T item : original.keySet()) {
       Set<T> rewrittenKeys = rewrite.apply(item);
       for (T rewrittenKey : rewrittenKeys) {
         result
-            .computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
+            .computeIfAbsent(rewrittenKey, k -> ProgramMethodSet.create())
             .addAll(original.get(item));
       }
     }
@@ -1053,7 +1055,7 @@
   public DexEncodedMethod lookupSingleTarget(
       Type type,
       DexMethod target,
-      DexType invocationContext,
+      ProgramMethod context,
       LibraryModeledPredicate modeledPredicate) {
     assert checkIfObsolete();
     DexType holder = target.holder;
@@ -1062,15 +1064,15 @@
     }
     switch (type) {
       case VIRTUAL:
-        return lookupSingleVirtualTarget(target, invocationContext, false, modeledPredicate);
+        return lookupSingleVirtualTarget(target, context, false, modeledPredicate);
       case INTERFACE:
-        return lookupSingleVirtualTarget(target, invocationContext, true, modeledPredicate);
+        return lookupSingleVirtualTarget(target, context, true, modeledPredicate);
       case DIRECT:
-        return lookupDirectTarget(target, invocationContext);
+        return lookupDirectTarget(target, context);
       case STATIC:
-        return lookupStaticTarget(target, invocationContext);
+        return lookupStaticTarget(target, context);
       case SUPER:
-        return lookupSuperTarget(target, invocationContext);
+        return lookupSuperTarget(target, context);
       default:
         return null;
     }
@@ -1079,44 +1081,39 @@
   public ProgramMethod lookupSingleProgramTarget(
       Type type,
       DexMethod target,
-      DexType invocationContext,
+      ProgramMethod context,
       LibraryModeledPredicate modeledPredicate) {
-    return asProgramMethodOrNull(
-        lookupSingleTarget(type, target, invocationContext, modeledPredicate), this);
+    return asProgramMethodOrNull(lookupSingleTarget(type, target, context, modeledPredicate), this);
   }
 
   /** For mapping invoke virtual instruction to single target method. */
   public DexEncodedMethod lookupSingleVirtualTarget(
-      DexMethod method, DexType invocationContext, boolean isInterface) {
+      DexMethod method, ProgramMethod context, boolean isInterface) {
     assert checkIfObsolete();
     return lookupSingleVirtualTarget(
-        method, invocationContext, isInterface, type -> false, method.holder, null);
+        method, context, isInterface, type -> false, method.holder, null);
   }
 
   /** For mapping invoke virtual instruction to single target method. */
   public DexEncodedMethod lookupSingleVirtualTarget(
       DexMethod method,
-      DexType invocationContext,
+      ProgramMethod context,
       boolean isInterface,
       LibraryModeledPredicate modeledPredicate) {
     assert checkIfObsolete();
     return lookupSingleVirtualTarget(
-        method, invocationContext, isInterface, modeledPredicate, method.holder, null);
+        method, context, isInterface, modeledPredicate, method.holder, null);
   }
 
   public DexEncodedMethod lookupSingleVirtualTarget(
       DexMethod method,
-      DexType invocationContext,
+      ProgramMethod context,
       boolean isInterface,
       LibraryModeledPredicate modeledPredicate,
       DexType refinedReceiverType,
       ClassTypeElement receiverLowerBoundType) {
     assert checkIfObsolete();
     assert refinedReceiverType != null;
-
-    DexProgramClass invocationClass = asProgramClassOrNull(definitionFor(invocationContext));
-    assert invocationClass != null;
-
     if (!refinedReceiverType.isClassType()) {
       // The refined receiver is not of class type and we will not be able to find a single target
       // (it is either primitive or array).
@@ -1138,9 +1135,9 @@
       return cachedItem;
     }
     SingleResolutionResult resolution =
-        resolveMethod(initialResolutionHolder, method).asSingleResolution();
+        resolveMethodOn(initialResolutionHolder, method).asSingleResolution();
     if (resolution == null
-        || resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this).isFalse()) {
+        || resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), this).isFalse()) {
       return null;
     }
     // If the method is modeled, return the resolution.
@@ -1189,7 +1186,7 @@
     LookupResultSuccess lookupResult =
         resolution
             .lookupVirtualDispatchTargets(
-                invocationClass, this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
+                context.getHolder(), this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
             .asLookupResultSuccess();
     if (lookupResult != null && !lookupResult.isIncomplete()) {
       LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
@@ -1394,7 +1391,7 @@
                 return TraversalContinuation.BREAK;
               } else {
                 SingleResolutionResult resolution =
-                    resolveMethod(clazz, dexItemFactory().objectMembers.finalize)
+                    resolveMethodOn(clazz, dexItemFactory().objectMembers.finalize)
                         .asSingleResolution();
                 if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
                   return TraversalContinuation.BREAK;
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 9604389..f2dddae 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -55,11 +56,13 @@
 import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
+import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.LookupLambdaTarget;
 import com.android.tools.r8.graph.LookupTarget;
 import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
 import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
@@ -84,6 +87,7 @@
 import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
 import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction;
@@ -103,6 +107,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.ProgramFieldSet;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableSet;
@@ -184,12 +189,13 @@
   private ProguardClassFilter dontWarnPatterns;
   private final EnqueuerUseRegistryFactory useRegistryFactory;
   private AnnotationRemover.Builder annotationRemoverBuilder;
+  private final DexDefinitionSupplier enqueuerDefinitionSupplier;
 
-  private final Map<DexMethod, Set<DexEncodedMethod>> virtualInvokes = new IdentityHashMap<>();
-  private final Map<DexMethod, Set<DexEncodedMethod>> interfaceInvokes = new IdentityHashMap<>();
-  private final Map<DexMethod, Set<DexEncodedMethod>> superInvokes = new IdentityHashMap<>();
-  private final Map<DexMethod, Set<DexEncodedMethod>> directInvokes = new IdentityHashMap<>();
-  private final Map<DexMethod, Set<DexEncodedMethod>> staticInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, ProgramMethodSet> virtualInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, ProgramMethodSet> interfaceInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, ProgramMethodSet> superInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, ProgramMethodSet> directInvokes = new IdentityHashMap<>();
+  private final Map<DexMethod, ProgramMethodSet> staticInvokes = new IdentityHashMap<>();
   private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
       new FieldAccessInfoCollectionImpl();
   private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
@@ -204,7 +210,7 @@
   private final Map<DexEncodedMethod, ProgramMethodSet> superInvokeDependencies =
       Maps.newIdentityHashMap();
   /** Set of instance fields that can be reached by read/write operations. */
-  private final Map<DexProgramClass, SetWithReason<DexEncodedField>> reachableInstanceFields =
+  private final Map<DexProgramClass, ProgramFieldSet> reachableInstanceFields =
       Maps.newIdentityHashMap();
 
   /**
@@ -272,7 +278,7 @@
   /**
    * Set of fields that belong to live classes and can be reached by invokes. These need to be kept.
    */
-  private final SetWithReason<DexEncodedField> liveFields;
+  private final LiveFieldsSet liveFields;
 
   /**
    * Set of service types (from META-INF/services/) that may have been instantiated reflectively via
@@ -361,7 +367,6 @@
         && mode.isInitialOrFinalTreeShaking()) {
       registerAnalysis(new ProtoEnqueuerExtension(appView));
     }
-
     liveTypes = new SetWithReportedReason<>();
     initializedTypes = new SetWithReportedReason<>();
     targetedMethods = new SetWithReason<>(graphReporter::registerMethod);
@@ -370,7 +375,7 @@
     // likely contain two methods. Thus the default capacity of 2.
     failedResolutionTargets = SetUtils.newIdentityHashSet(2);
     liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
-    liveFields = new SetWithReason<>(graphReporter::registerField);
+    liveFields = new LiveFieldsSet(graphReporter::registerField);
     lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
 
     objectAllocationInfoCollection =
@@ -383,6 +388,8 @@
     } else {
       desugaredLibraryWrapperAnalysis = null;
     }
+
+    enqueuerDefinitionSupplier = new EnqueuerDefinitionSupplier(appView, this);
   }
 
   private AppInfoWithClassHierarchy appInfo() {
@@ -484,6 +491,33 @@
     recordTypeReference(field.type);
   }
 
+  public DexDefinition definitionFor(DexReference reference) {
+    if (reference.isDexType()) {
+      return definitionFor(reference.asDexType());
+    } else if (reference.isDexMethod()) {
+      return definitionFor(reference.asDexMethod());
+    } else {
+      assert reference.isDexField();
+      return definitionFor(reference.asDexField());
+    }
+  }
+
+  public DexEncodedField definitionFor(DexField field) {
+    DexClass clazz = definitionFor(field.holder);
+    if (clazz == null) {
+      return null;
+    }
+    return clazz.lookupField(field);
+  }
+
+  public DexEncodedMethod definitionFor(DexMethod method) {
+    DexClass clazz = definitionFor(method.holder);
+    if (clazz == null) {
+      return null;
+    }
+    return clazz.lookupMethod(method);
+  }
+
   private DexClass definitionFor(DexType type) {
     DexClass clazz = appView.definitionFor(type);
     if (clazz == null) {
@@ -610,13 +644,12 @@
         }
       }
     } else if (item.isDexEncodedField()) {
-      DexEncodedField dexEncodedField = item.asDexEncodedField();
-      DexProgramClass holder = getProgramClassOrNull(dexEncodedField.holder());
+      DexEncodedField field = item.asDexEncodedField();
+      DexProgramClass holder = getProgramClassOrNull(field.holder());
       if (holder != null) {
         workList.enqueueMarkFieldKeptAction(
-            holder,
-            dexEncodedField,
-            graphReporter.reportKeepField(precondition, rules, dexEncodedField));
+            new ProgramField(holder, field),
+            graphReporter.reportKeepField(precondition, rules, field));
       }
     } else if (item.isDexEncodedMethod()) {
       DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
@@ -672,47 +705,42 @@
   //
 
   private boolean registerMethodWithTargetAndContext(
-      Map<DexMethod, Set<DexEncodedMethod>> seen, DexMethod method, ProgramMethod context) {
+      Map<DexMethod, ProgramMethodSet> seen, DexMethod method, ProgramMethod context) {
     DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory());
     if (baseHolder.isClassType()) {
       markTypeAsLive(baseHolder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
-      return seen.computeIfAbsent(method, ignore -> Sets.newIdentityHashSet())
-          .add(context.getDefinition());
+      return seen.computeIfAbsent(method, ignore -> ProgramMethodSet.create()).add(context);
     }
     return false;
   }
 
   public boolean registerFieldRead(DexField field, ProgramMethod context) {
-    return registerFieldAccess(field, context.getDefinition(), true, false);
-  }
-
-  public boolean registerFieldReadFromAnnotation(DexField field) {
-    return registerFieldAccess(field, DexEncodedMethod.ANNOTATION_REFERENCE, true, false);
+    return registerFieldAccess(field, context, true, false);
   }
 
   public boolean registerReflectiveFieldRead(DexField field, ProgramMethod context) {
-    return registerFieldAccess(field, context.getDefinition(), true, true);
+    return registerFieldAccess(field, context, true, true);
   }
 
   public boolean registerFieldWrite(DexField field, ProgramMethod context) {
-    return registerFieldAccess(field, context.getDefinition(), false, false);
+    return registerFieldAccess(field, context, false, false);
   }
 
   public boolean registerReflectiveFieldWrite(DexField field, ProgramMethod context) {
-    return registerFieldAccess(field, context.getDefinition(), false, true);
+    return registerFieldAccess(field, context, false, true);
   }
 
   public boolean registerReflectiveFieldAccess(DexField field, ProgramMethod context) {
-    boolean changed = registerFieldAccess(field, context.getDefinition(), true, true);
-    changed |= registerFieldAccess(field, context.getDefinition(), false, true);
+    boolean changed = registerFieldAccess(field, context, true, true);
+    changed |= registerFieldAccess(field, context, false, true);
     return changed;
   }
 
   private boolean registerFieldAccess(
-      DexField field, DexEncodedMethod context, boolean isRead, boolean isReflective) {
+      DexField field, ProgramMethod context, boolean isRead, boolean isReflective) {
     FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field);
     if (info == null) {
-      DexEncodedField encodedField = resolveField(field);
+      DexEncodedField encodedField = resolveField(field).getResolvedField();
 
       // If the field does not exist, then record this in the mapping, such that we don't have to
       // resolve the field the next time.
@@ -751,8 +779,7 @@
       bootstrapMethods.add(callSite.bootstrapMethod.asMethod());
     }
 
-    DexProgramClass contextHolder = context.getHolder();
-    LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, contextHolder);
+    LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, context);
     if (descriptor == null) {
       return;
     }
@@ -762,14 +789,13 @@
       assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
       CfCode code = contextMethod.getCode().asCfCode();
       if (code != null) {
-        LambdaClass lambdaClass =
-            lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.holder());
+        LambdaClass lambdaClass = lambdaRewriter.getOrCreateLambdaClass(descriptor, context);
         lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context));
         lambdaCallSites
             .computeIfAbsent(contextMethod, k -> new IdentityHashMap<>())
             .put(callSite, lambdaClass);
         if (lambdaClass.descriptor.interfaces.contains(appView.dexItemFactory().serializableType)) {
-          classesWithSerializableLambdas.add(contextHolder);
+          classesWithSerializableLambdas.add(context.getHolder());
         }
       }
       if (descriptor.delegatesToLambdaImplMethod()) {
@@ -1137,43 +1163,45 @@
   }
 
   private boolean traceInstanceFieldRead(
-      DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) {
-    if (!registerFieldRead(field, currentMethod)) {
+      DexField fieldReference, ProgramMethod currentMethod, boolean fromMethodHandle) {
+    if (!registerFieldRead(fieldReference, currentMethod)) {
       return false;
     }
 
     // Must mark the field as targeted even if it does not exist.
-    markFieldAsTargeted(field, currentMethod);
+    markFieldAsTargeted(fieldReference, currentMethod);
 
-    DexEncodedField encodedField = resolveField(field);
-    if (encodedField == null) {
+    FieldResolutionResult resolutionResult = resolveField(fieldReference);
+    if (resolutionResult.isFailedOrUnknownResolution()) {
+      return false;
+    }
+
+    ProgramField field =
+        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    if (field == null) {
+      // No need to trace into the non-program code.
       return false;
     }
 
     if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
-    }
-
-    DexProgramClass clazz = getProgramClassOrNull(encodedField.holder());
-    if (clazz == null) {
-      return false;
+      fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
     }
 
     if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Iget `%s`.", field);
+      Log.verbose(getClass(), "Register Iget `%s`.", fieldReference);
     }
 
     // If unused interface removal is enabled, then we won't necessarily mark the actual holder of
     // the field as live, if the holder is an interface.
     if (appView.options().enableUnusedInterfaceRemoval) {
-      if (encodedField.field != field) {
-        markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
-        markTypeAsLive(encodedField.field.type, classReferencedFromReporter(currentMethod));
+      if (field.getReference() != fieldReference) {
+        markTypeAsLive(
+            field.getHolder(),
+            graphReporter.reportClassReferencedFrom(field.getHolder(), currentMethod));
       }
     }
 
-    workList.enqueueMarkReachableFieldAction(
-        clazz, encodedField, KeepReason.fieldReferencedIn(currentMethod));
+    workList.enqueueMarkReachableFieldAction(field, KeepReason.fieldReferencedIn(currentMethod));
     return true;
   }
 
@@ -1186,43 +1214,46 @@
   }
 
   private boolean traceInstanceFieldWrite(
-      DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) {
-    if (!registerFieldWrite(field, currentMethod)) {
+      DexField fieldReference, ProgramMethod currentMethod, boolean fromMethodHandle) {
+    if (!registerFieldWrite(fieldReference, currentMethod)) {
       return false;
     }
 
     // Must mark the field as targeted even if it does not exist.
-    markFieldAsTargeted(field, currentMethod);
+    markFieldAsTargeted(fieldReference, currentMethod);
 
-    DexEncodedField encodedField = resolveField(field);
-    if (encodedField == null) {
+    FieldResolutionResult resolutionResult = resolveField(fieldReference);
+    if (resolutionResult.isFailedOrUnknownResolution()) {
+      return false;
+    }
+
+    ProgramField field =
+        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    if (field == null) {
+      // No need to trace into the non-program code.
       return false;
     }
 
     if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
-    }
-
-    DexProgramClass clazz = getProgramClassOrNull(encodedField.holder());
-    if (clazz == null) {
-      return false;
+      fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
     }
 
     if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Iput `%s`.", field);
+      Log.verbose(getClass(), "Register Iput `%s`.", fieldReference);
     }
 
     // If unused interface removal is enabled, then we won't necessarily mark the actual holder of
     // the field as live, if the holder is an interface.
     if (appView.options().enableUnusedInterfaceRemoval) {
-      if (encodedField.field != field) {
-        markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
-        markTypeAsLive(encodedField.field.type, classReferencedFromReporter(currentMethod));
+      if (field.getReference() != fieldReference) {
+        markTypeAsLive(
+            field.getHolder(),
+            graphReporter.reportClassReferencedFrom(field.getHolder(), currentMethod));
       }
     }
 
     KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
-    workList.enqueueMarkReachableFieldAction(clazz, encodedField, reason);
+    workList.enqueueMarkReachableFieldAction(field, reason);
     return true;
   }
 
@@ -1235,30 +1266,31 @@
   }
 
   private boolean traceStaticFieldRead(
-      DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) {
-    if (!registerFieldRead(field, currentMethod)) {
+      DexField fieldReference, ProgramMethod currentMethod, boolean fromMethodHandle) {
+    if (!registerFieldRead(fieldReference, currentMethod)) {
       return false;
     }
 
-    DexEncodedField encodedField = resolveField(field);
-    if (encodedField == null) {
+    FieldResolutionResult resolutionResult = resolveField(fieldReference);
+    if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must mark the field as targeted even if it does not exist.
-      markFieldAsTargeted(field, currentMethod);
+      markFieldAsTargeted(fieldReference, currentMethod);
       return false;
     }
 
-    if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
-    }
-
-    DexProgramClass holder = getProgramClassOrNull(encodedField.holder());
-    if (holder == null) {
+    ProgramField field =
+        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    if (field == null) {
       // No need to trace into the non-program code.
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
+    }
+
     if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Sget `%s`.", field);
+      Log.verbose(getClass(), "Register Sget `%s`.", fieldReference);
     }
 
     if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
@@ -1266,22 +1298,21 @@
       boolean skipTracing =
           appView.withGeneratedExtensionRegistryShrinker(
               shrinker ->
-                  shrinker.isDeadProtoExtensionField(
-                      encodedField, fieldAccessInfoCollection, pinnedItems),
+                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, pinnedItems),
               false);
       if (skipTracing) {
-        addDeadProtoTypeCandidate(holder);
+        addDeadProtoTypeCandidate(field.getHolder());
         return false;
       }
     }
 
-    if (encodedField.field != field) {
+    if (field.getReference() != fieldReference) {
       // Mark the non-rebound field access as targeted. Note that this should only be done if the
       // field is not a dead proto field (in which case we bail-out above).
-      markFieldAsTargeted(field, currentMethod);
+      markFieldAsTargeted(fieldReference, currentMethod);
     }
 
-    markStaticFieldAsLive(encodedField, KeepReason.fieldReferencedIn(currentMethod));
+    markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
     return true;
   }
 
@@ -1294,30 +1325,31 @@
   }
 
   private boolean traceStaticFieldWrite(
-      DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) {
-    if (!registerFieldWrite(field, currentMethod)) {
+      DexField fieldReference, ProgramMethod currentMethod, boolean fromMethodHandle) {
+    if (!registerFieldWrite(fieldReference, currentMethod)) {
       return false;
     }
 
-    DexEncodedField encodedField = resolveField(field);
-    if (encodedField == null) {
+    FieldResolutionResult resolutionResult = resolveField(fieldReference);
+    if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must mark the field as targeted even if it does not exist.
-      markFieldAsTargeted(field, currentMethod);
+      markFieldAsTargeted(fieldReference, currentMethod);
       return false;
     }
 
-    if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
-    }
-
-    DexProgramClass holder = getProgramClassOrNull(encodedField.holder());
-    if (holder == null) {
+    ProgramField field =
+        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    if (field == null) {
       // No need to trace into the non-program code.
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
+    }
+
     if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Sput `%s`.", field);
+      Log.verbose(getClass(), "Register Sput `%s`.", fieldReference);
     }
 
     if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
@@ -1325,22 +1357,21 @@
       boolean skipTracing =
           appView.withGeneratedExtensionRegistryShrinker(
               shrinker ->
-                  shrinker.isDeadProtoExtensionField(
-                      encodedField, fieldAccessInfoCollection, pinnedItems),
+                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, pinnedItems),
               false);
       if (skipTracing) {
-        addDeadProtoTypeCandidate(holder);
+        addDeadProtoTypeCandidate(field.getHolder());
         return false;
       }
     }
 
-    if (encodedField.field != field) {
+    if (field.getReference() != fieldReference) {
       // Mark the non-rebound field access as targeted. Note that this should only be done if the
       // field is not a dead proto field (in which case we bail-out above).
-      markFieldAsTargeted(field, currentMethod);
+      markFieldAsTargeted(fieldReference, currentMethod);
     }
 
-    markStaticFieldAsLive(encodedField, KeepReason.fieldReferencedIn(currentMethod));
+    markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
     return true;
   }
 
@@ -1505,7 +1536,8 @@
     compatEnqueueHolderIfDependentNonStaticMember(
         holder, rootSet.getDependentKeepClassCompatRule(holder.getType()));
 
-    analyses.forEach(analysis -> analysis.processNewlyLiveClass(holder, workList));
+    analyses.forEach(
+        analysis -> analysis.processNewlyLiveClass(holder, workList, enqueuerDefinitionSupplier));
   }
 
   private void ensureMethodsContinueToWidenAccess(DexClass clazz) {
@@ -1616,22 +1648,21 @@
     annotation.annotation.collectIndexedItems(referenceMarker);
   }
 
-  private DexEncodedField resolveField(DexField field) {
+  private FieldResolutionResult resolveField(DexField field) {
     // Record the references in case they are not program types.
     recordTypeReference(field.holder);
     recordTypeReference(field.type);
-    DexEncodedField encodedField = appInfo.resolveField(field);
-    if (encodedField == null) {
+    FieldResolutionResult resolutionResult = appInfo.resolveField(field);
+    if (resolutionResult.isFailedOrUnknownResolution()) {
       reportMissingField(field);
-      return null;
     }
-    return encodedField;
+    return resolutionResult;
   }
 
   private SingleResolutionResult resolveMethod(DexMethod method, KeepReason reason) {
     // Record the references in case they are not program types.
     recordMethodReference(method);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
     if (resolutionResult.isFailedResolution()) {
       reportMissingMethod(method);
       markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
@@ -1643,8 +1674,7 @@
       DexMethod method, KeepReason reason, boolean interfaceInvoke) {
     // Record the references in case they are not program types.
     recordMethodReference(method);
-    ResolutionResult resolutionResult =
-        appInfo.resolveMethod(method.holder, method, interfaceInvoke);
+    ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
     if (resolutionResult.isFailedResolution()) {
       reportMissingMethod(method);
       markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
@@ -1998,7 +2028,7 @@
       assert method.holder == superClass.type;
       if (seenMethods.add(MethodSignatureEquivalence.get().wrap(method))) {
         SingleResolutionResult resolution =
-            appInfo.resolveMethod(superClass, method).asSingleResolution();
+            appInfo.resolveMethodOn(superClass, method).asSingleResolution();
         assert resolution != null;
         assert resolution.getResolvedHolder().isProgramClass();
         if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
@@ -2052,7 +2082,7 @@
       // Note: It would be reasonable to not process methods already seen during the marking of
       // program usages, but that would cause the methods to not be marked as library overrides.
       markLibraryOrClasspathOverrideLive(
-          instantiation, libraryClass, appInfo.resolveMethod(libraryClass, method.method));
+          instantiation, libraryClass, appInfo.resolveMethodOn(libraryClass, method.method));
 
       // Due to API conversion, some overrides can be hidden since they will be rewritten. See
       // class comment of DesugaredLibraryAPIConverter and vivifiedType logic.
@@ -2068,7 +2098,7 @@
         markLibraryOrClasspathOverrideLive(
             instantiation,
             libraryClass,
-            appInfo.resolveMethod(instantiation.asClass(), methodToResolve));
+            appInfo.resolveMethodOn(instantiation.asClass(), methodToResolve));
       }
     }
   }
@@ -2123,12 +2153,11 @@
    */
   private void transitionFieldsForInstantiatedClass(DexProgramClass clazz) {
     do {
-      SetWithReason<DexEncodedField> reachableFields = reachableInstanceFields.get(clazz);
+      ProgramFieldSet reachableFields = reachableInstanceFields.get(clazz);
       if (reachableFields != null) {
-        for (DexEncodedField field : reachableFields.getItems()) {
-          // TODO(b/120959039): Should the reason this field is reachable come from the set?
-          markInstanceFieldAsLive(clazz, field, KeepReason.reachableFromLiveType(clazz.type));
-        }
+        // TODO(b/120959039): Should the reason this field is reachable come from the set?
+        KeepReason reason = KeepReason.reachableFromLiveType(clazz.type);
+        reachableFields.forEach(field -> markInstanceFieldAsLive(field, reason));
       }
       clazz = getProgramClassOrNull(clazz.superType);
     } while (clazz != null && !objectAllocationInfoCollection.isInstantiatedDirectly(clazz));
@@ -2176,58 +2205,48 @@
     markTypeAsLive(field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
   }
 
-  private void markStaticFieldAsLive(DexEncodedField encodedField, KeepReason reason) {
+  private void markStaticFieldAsLive(ProgramField field, KeepReason reason) {
     // Mark the type live here, so that the class exists at runtime.
-    DexField field = encodedField.field;
     markTypeAsLive(
-        field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
+        field.getHolder(), graphReporter.reportClassReferencedFrom(field.getHolder(), field));
     markTypeAsLive(
-        field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
+        field.getReference().type, clazz -> graphReporter.reportClassReferencedFrom(clazz, field));
 
-    DexProgramClass clazz = getProgramClassOrNull(field.holder);
-    if (clazz == null) {
-      return;
-    }
-
-    markDirectAndIndirectClassInitializersAsLive(clazz);
+    markDirectAndIndirectClassInitializersAsLive(field.getHolder());
 
     // This field might be an instance field reachable from a static context, e.g. a getStatic that
     // resolves to an instance field. We have to keep the instance field nonetheless, as otherwise
     // we might unmask a shadowed static field and hence change semantics.
-    if (encodedField.accessFlags.isStatic()) {
+    if (field.getDefinition().isStatic()) {
       if (Log.ENABLED) {
-        Log.verbose(getClass(), "Adding static field `%s` to live set.", encodedField.field);
+        Log.verbose(getClass(), "Adding static field `%s` to live set.", field);
       }
     } else {
       if (Log.ENABLED) {
-        Log.verbose(getClass(), "Adding instance field `%s` to live set (static context).",
-            encodedField.field);
+        Log.verbose(getClass(), "Adding instance field `%s` to live set (static context).", field);
       }
     }
-    processAnnotations(clazz, encodedField);
-    liveFields.add(encodedField, reason);
-
-    // Add all dependent members to the workqueue.
-    enqueueRootItems(rootSet.getDependentItems(encodedField));
-
-    // Notify analyses.
-    analyses.forEach(analysis -> analysis.processNewlyLiveField(encodedField));
-  }
-
-  private void markInstanceFieldAsLive(
-      DexProgramClass holder, DexEncodedField field, KeepReason reason) {
-    assert field != null;
-    assert field.isProgramField(appView);
-    markTypeAsLive(field.holder(), reason);
-    markTypeAsLive(field.field.type, reason);
-    if (Log.ENABLED) {
-      Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
-    }
-    processAnnotations(holder, field);
+    processAnnotations(field.getHolder(), field.getDefinition());
     liveFields.add(field, reason);
 
     // Add all dependent members to the workqueue.
-    enqueueRootItems(rootSet.getDependentItems(field));
+    enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
+
+    // Notify analyses.
+    analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
+  }
+
+  private void markInstanceFieldAsLive(ProgramField field, KeepReason reason) {
+    markTypeAsLive(field.getHolder(), graphReporter.registerClass(field.getHolder(), reason));
+    markTypeAsLive(field.getReference().type, reason);
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Adding instance field `%s` to live set.", field);
+    }
+    processAnnotations(field.getHolder(), field.getDefinition());
+    liveFields.add(field, reason);
+
+    // Add all dependent members to the workqueue.
+    enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
 
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
@@ -2264,28 +2283,27 @@
     return info != null;
   }
 
-  public boolean isFieldLive(DexEncodedField field) {
+  public boolean isFieldLive(ProgramField field) {
     return liveFields.contains(field);
   }
 
-  public boolean isFieldRead(DexEncodedField field) {
-    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+  public boolean isFieldRead(ProgramField field) {
+    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.getReference());
     return info != null && info.isRead();
   }
 
   public boolean isFieldWrittenInMethodSatisfying(
-      DexEncodedField field, Predicate<DexEncodedMethod> predicate) {
-    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+      ProgramField field, Predicate<ProgramMethod> predicate) {
+    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.getReference());
     return info != null && info.isWrittenInMethodSatisfying(predicate);
   }
 
-  public boolean isFieldWrittenOutsideDefaultConstructor(DexEncodedField field) {
-    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+  public boolean isFieldWrittenOutsideDefaultConstructor(ProgramField field) {
+    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.getReference());
     if (info == null) {
       return false;
     }
-    DexClass clazz = appView.definitionFor(field.holder());
-    DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
+    DexEncodedMethod defaultInitializer = field.getHolder().getDefaultInitializer();
     return defaultInitializer != null
         ? info.isWrittenOutside(defaultInitializer)
         : info.isWritten();
@@ -2322,37 +2340,30 @@
   }
 
   // Package protected due to entry point from worklist.
-  void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) {
-    DexField field = encodedField.field;
+  void markInstanceFieldAsReachable(ProgramField field, KeepReason reason) {
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
     }
 
     markTypeAsLive(
-        field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
+        field.getHolder(), graphReporter.reportClassReferencedFrom(field.getHolder(), field));
     markTypeAsLive(
-        field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, encodedField));
-
-    DexProgramClass clazz = getProgramClassOrNull(field.holder);
-    if (clazz == null) {
-      return;
-    }
+        field.getReference().type, clazz -> graphReporter.reportClassReferencedFrom(clazz, field));
 
     // We might have a instance field access that is dispatched to a static field. In such case,
     // we have to keep the static field, so that the dispatch fails at runtime in the same way that
     // it did before. We have to keep the field even if the receiver has no live inhabitants, as
     // field resolution happens before the receiver is inspected.
-    if (encodedField.accessFlags.isStatic()) {
-      markStaticFieldAsLive(encodedField, reason);
+    if (field.getDefinition().isStatic()) {
+      markStaticFieldAsLive(field, reason);
+    } else if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
+        field.getHolder())) {
+      markInstanceFieldAsLive(field, reason);
     } else {
-      if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(clazz)) {
-        markInstanceFieldAsLive(clazz, encodedField, reason);
-      } else {
-        // Add the field to the reachable set if the type later becomes instantiated.
-        reachableInstanceFields
-            .computeIfAbsent(clazz, ignore -> newSetWithoutReasonReporter())
-            .add(encodedField, reason);
-      }
+      // Add the field to the reachable set if the type later becomes instantiated.
+      reachableInstanceFields
+          .computeIfAbsent(field.getHolder(), ignore -> ProgramFieldSet.create())
+          .add(field);
     }
   }
 
@@ -2407,9 +2418,9 @@
     DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
     markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
 
-    DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder();
+    DexProgramClass contextHolder = contextOrNull != null ? contextOrNull.getHolder() : null;
     if (contextOrNull != null
-        && resolution.isAccessibleForVirtualDispatchFrom(context, appInfo).isFalse()) {
+        && resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
       // Not accessible from this context, so this call will cause a runtime exception.
       return;
     }
@@ -2417,7 +2428,7 @@
     // If the resolved method is not a virtual target, eg, is static, dispatch will fail too.
     if (!resolvedMethod.isVirtualMethod()) {
       // This can only happen when context is null, otherwise the access check above will fail.
-      assert context == null;
+      assert contextOrNull == null;
       return;
     }
 
@@ -2426,7 +2437,7 @@
 
     resolution
         .lookupVirtualDispatchTargets(
-            context,
+            contextHolder,
             appInfo,
             (type, subTypeConsumer, lambdaConsumer) ->
                 objectAllocationInfoCollection.forEachInstantiatedSubType(
@@ -2567,6 +2578,10 @@
       throws ExecutionException {
     this.rootSet = rootSet;
     this.dontWarnPatterns = dontWarnPatterns;
+    if (!options.kotlinOptimizationOptions().disableKotlinSpecificOptimizations
+        && mode.isInitialTreeShaking()) {
+      registerAnalysis(new KotlinMetadataEnqueuerExtension(appView));
+    }
     // Translate the result of root-set computation into enqueuer actions.
     if (appView.options().isShrinking() || appView.options().getProguardConfiguration() == null) {
       enqueueRootItems(rootSet.noShrinking);
@@ -2820,7 +2835,11 @@
         new AppInfoWithLiveness(
             app,
             SetUtils.mapIdentityHashSet(deadProtoTypeCandidates, DexProgramClass::getType),
-            missingTypes,
+            // TODO(b/155959821): We should be able to assert that missing types is a subset of
+            //   initialMissingTypes + synthesized types.
+            mode.isFinalTreeShaking()
+                ? Sets.union(initialMissingTypes, missingTypes)
+                : missingTypes,
             SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
             Collections.unmodifiableSet(instantiatedAppServices),
             Enqueuer.toSortedDescriptorSet(targetedMethods.getItems()),
@@ -3143,7 +3162,7 @@
   private long getNumberOfLiveItems() {
     long result = liveTypes.items.size();
     result += liveMethods.items.size();
-    result += liveFields.items.size();
+    result += liveFields.fields.size();
     return result;
   }
 
@@ -3281,12 +3300,11 @@
   }
 
   // Package protected due to entry point from worklist.
-  void markFieldAsKept(DexProgramClass holder, DexEncodedField target, KeepReason reason) {
-    assert holder.type == target.holder();
-    if (target.accessFlags.isStatic()) {
-      markStaticFieldAsLive(target, reason);
+  void markFieldAsKept(ProgramField field, KeepReason reason) {
+    if (field.getDefinition().isStatic()) {
+      markStaticFieldAsLive(field, reason);
     } else {
-      markInstanceFieldAsReachable(target, reason);
+      markInstanceFieldAsReachable(field, reason);
     }
   }
 
@@ -3504,7 +3522,7 @@
       if (clazz == null) {
         return;
       }
-      DexEncodedField encodedField = appView.definitionFor(field);
+      DexEncodedField encodedField = clazz.lookupField(field);
       if (encodedField == null) {
         return;
       }
@@ -3514,14 +3532,14 @@
       // fields since the creation of a field updater throws a NoSuchFieldException if the field
       // is not present.
       boolean keepClass =
-          !encodedField.accessFlags.isStatic()
+          !encodedField.isStatic()
               && dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
       if (keepClass) {
         workList.enqueueMarkInstantiatedAction(
             clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
       }
       if (pinnedItems.add(encodedField.field)) {
-        markFieldAsKept(clazz, encodedField, KeepReason.reflectiveUseIn(method));
+        markFieldAsKept(new ProgramField(clazz, encodedField), KeepReason.reflectiveUseIn(method));
       }
     } else {
       assert identifierItem.isDexMethod();
@@ -3817,6 +3835,32 @@
     }
   }
 
+  private class LiveFieldsSet {
+
+    private final Set<DexEncodedField> fields = Sets.newIdentityHashSet();
+
+    private final BiConsumer<DexEncodedField, KeepReason> register;
+
+    LiveFieldsSet(BiConsumer<DexEncodedField, KeepReason> register) {
+      this.register = register;
+    }
+
+    boolean add(ProgramField field, KeepReason reason) {
+      DexEncodedField definition = field.getDefinition();
+      register.accept(definition, reason);
+      transitionUnusedInterfaceToLive(field.getHolder());
+      return fields.add(definition);
+    }
+
+    boolean contains(DexEncodedField field) {
+      return fields.contains(field);
+    }
+
+    boolean contains(ProgramField field) {
+      return contains(field.getDefinition());
+    }
+  }
+
   private class LiveMethodsSet {
 
     private final Set<DexEncodedMethod> items = Sets.newIdentityHashSet();
@@ -3890,37 +3934,40 @@
     }
 
     @Override
-    public boolean addField(DexField field) {
-      recordTypeReference(field.holder);
-      recordTypeReference(field.type);
-      DexClass holder = appView.definitionFor(field.holder);
+    public boolean addField(DexField fieldReference) {
+      recordFieldReference(fieldReference);
+      DexProgramClass holder = getProgramClassOrNull(fieldReference.holder);
       if (holder == null) {
         return false;
       }
-      DexEncodedField target = holder.lookupStaticField(field);
-      if (target != null) {
-        // There is no dispatch on annotations, so only keep what is directly referenced.
-        if (target.field == field) {
-          if (!registerFieldReadFromAnnotation(field)) {
-            return false;
-          }
-          markStaticFieldAsLive(target, KeepReason.referencedInAnnotation(annotationHolder));
-          // When an annotation has a field of an enum type with a default value then Java VM
-          // will use the values() method on that enum class.
-          if (options.isGeneratingClassFiles()
-              && annotationHolder == dexItemFactory.annotationDefault) {
-            DexProgramClass clazz = getProgramClassOrNull(field.type);
-            if (clazz != null && clazz.accessFlags.isEnum()) {
-              markEnumValuesAsReachable(clazz, KeepReason.referencedInAnnotation(annotationHolder));
-            }
+      ProgramField field = holder.lookupProgramField(fieldReference);
+      if (field == null) {
+        return false;
+      }
+      // There is no dispatch on annotations, so only keep what is directly referenced.
+      if (field.getReference() != fieldReference) {
+        return false;
+      }
+      if (field.getDefinition().isStatic()) {
+        FieldAccessInfoImpl fieldAccessInfo =
+            fieldAccessInfoCollection.contains(fieldReference)
+                ? fieldAccessInfoCollection.get(fieldReference)
+                : fieldAccessInfoCollection.extend(
+                    fieldReference, new FieldAccessInfoImpl(fieldReference));
+        fieldAccessInfo.setReadFromAnnotation();
+        markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+        // When an annotation has a field of an enum type with a default value then Java VM
+        // will use the values() method on that enum class.
+        if (options.isGeneratingClassFiles()
+            && annotationHolder == dexItemFactory.annotationDefault) {
+          if (field.getHolder().isEnum()) {
+            markEnumValuesAsReachable(
+                field.getHolder(), KeepReason.referencedInAnnotation(annotationHolder));
           }
         }
       } else {
-        target = holder.lookupInstanceField(field);
         // There is no dispatch on annotations, so only keep what is directly referenced.
-        if (target != null && target.field != field) {
-          markInstanceFieldAsReachable(target, KeepReason.referencedInAnnotation(annotationHolder));
-        }
+        markInstanceFieldAsReachable(field, KeepReason.referencedInAnnotation(annotationHolder));
       }
       return false;
     }
@@ -3984,4 +4031,44 @@
     }
   }
 
+  public static class EnqueuerDefinitionSupplier implements DexDefinitionSupplier {
+
+    private final Enqueuer enqueuer;
+    private final AppView<?> appView;
+
+    private EnqueuerDefinitionSupplier(AppView<?> appView, Enqueuer enqueuer) {
+      this.appView = appView;
+      this.enqueuer = enqueuer;
+    }
+
+    @Override
+    public DexDefinition definitionFor(DexReference reference) {
+      return enqueuer.definitionFor(reference);
+    }
+
+    @Override
+    public DexEncodedField definitionFor(DexField field) {
+      return enqueuer.definitionFor(field);
+    }
+
+    @Override
+    public DexEncodedMethod definitionFor(DexMethod method) {
+      return enqueuer.definitionFor(method);
+    }
+
+    @Override
+    public DexClass definitionFor(DexType type) {
+      return enqueuer.definitionFor(type);
+    }
+
+    @Override
+    public DexProgramClass definitionForProgramType(DexType type) {
+      return enqueuer.getProgramClassOrNull(type);
+    }
+
+    @Override
+    public DexItemFactory dexItemFactory() {
+      return appView.dexItemFactory();
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index f5ffc75..3c9649d 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -5,11 +5,11 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
 import java.util.ArrayDeque;
@@ -52,17 +52,17 @@
   }
 
   static class MarkReachableFieldAction extends EnqueuerAction {
-    final DexEncodedField target;
+    final ProgramField field;
     final KeepReason reason;
 
-    public MarkReachableFieldAction(DexEncodedField target, KeepReason reason) {
-      this.target = target;
+    public MarkReachableFieldAction(ProgramField field, KeepReason reason) {
+      this.field = field;
       this.reason = reason;
     }
 
     @Override
     public void run(Enqueuer enqueuer) {
-      enqueuer.markInstanceFieldAsReachable(target, reason);
+      enqueuer.markInstanceFieldAsReachable(field, reason);
     }
   }
 
@@ -151,20 +151,17 @@
   }
 
   static class MarkFieldKeptAction extends EnqueuerAction {
-    final DexProgramClass holder;
-    final DexEncodedField target;
+    final ProgramField field;
     final KeepReasonWitness witness;
 
-    public MarkFieldKeptAction(
-        DexProgramClass holder, DexEncodedField target, KeepReasonWitness witness) {
-      this.holder = holder;
-      this.target = target;
+    public MarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
+      this.field = field;
       this.witness = witness;
     }
 
     @Override
     public void run(Enqueuer enqueuer) {
-      enqueuer.markFieldAsKept(holder, target, witness);
+      enqueuer.markFieldAsKept(field, witness);
     }
   }
 
@@ -255,9 +252,7 @@
     queue.add(new MarkReachableSuperAction(method, from));
   }
 
-  public void enqueueMarkReachableFieldAction(
-      DexProgramClass clazz, DexEncodedField field, KeepReason reason) {
-    assert field.holder() == clazz.type;
+  public void enqueueMarkReachableFieldAction(ProgramField field, KeepReason reason) {
     queue.add(new MarkReachableFieldAction(field, reason));
   }
 
@@ -293,10 +288,8 @@
     queue.add(new MarkMethodKeptAction(method, reason));
   }
 
-  void enqueueMarkFieldKeptAction(
-      DexProgramClass holder, DexEncodedField field, KeepReasonWitness witness) {
-    assert field.isProgramField(appView);
-    queue.add(new MarkFieldKeptAction(holder, field, witness));
+  void enqueueMarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
+    queue.add(new MarkFieldKeptAction(field, witness));
   }
 
   public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 0e39b4c..2ab539d 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -26,6 +26,7 @@
 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;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.references.Reference;
@@ -219,9 +220,9 @@
     return KeepReasonWitness.INSTANCE;
   }
 
-  public KeepReasonWitness reportClassReferencedFrom(DexProgramClass clazz, DexEncodedField field) {
+  public KeepReasonWitness reportClassReferencedFrom(DexProgramClass clazz, ProgramField field) {
     if (keptGraphConsumer != null) {
-      FieldGraphNode source = getFieldGraphNode(field.field);
+      FieldGraphNode source = getFieldGraphNode(field.getReference());
       ClassGraphNode target = getClassGraphNode(clazz.type);
       return reportEdge(source, target, EdgeKind.ReferencedFrom);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index f73139d..0979ef9 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -7,10 +7,10 @@
 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.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysis;
 import com.android.tools.r8.ir.analysis.escape.EscapeAnalysisConfiguration;
@@ -193,7 +193,7 @@
         AppView<?> appView,
         EscapeAnalysis escapeAnalysis,
         Instruction escapeRoute,
-        DexMethod context) {
+        ProgramMethod context) {
       if (appView.appInfo().hasLiveness()) {
         return isLegitimateConstructorInvocation(
             appView.withLiveness(), escapeAnalysis, escapeRoute, context);
@@ -205,7 +205,7 @@
         AppView<AppInfoWithLiveness> appView,
         EscapeAnalysis escapeAnalysis,
         Instruction instruction,
-        DexMethod context) {
+        ProgramMethod context) {
       if (!instruction.isInvokeDirect()) {
         return false;
       }
@@ -221,7 +221,7 @@
         }
       }
 
-      DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.holder);
+      DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
       if (singleTarget == null) {
         return false;
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index e105350..ffb940a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -378,7 +378,7 @@
       // is due to the lack of the definition or it indeed means no matching rules. Similar to how
       // we apply those assume rules, here we use a resolved target.
       DexEncodedMethod target =
-          appView.appInfo().resolveMethod(subType, referenceInSubType).getSingleTarget();
+          appView.appInfo().unsafeResolveMethodDueToDexFormat(referenceInSubType).getSingleTarget();
       // But, the resolution should not be landed on the current type we are visiting.
       if (target == null || target.holder() == type) {
         continue;
@@ -555,7 +555,7 @@
 
     private void tryAndKeepMethodOnClass(DexEncodedMethod method, ProguardMemberRule rule) {
       SingleResolutionResult resolutionResult =
-          appView.appInfo().resolveMethod(originalClazz, method.method).asSingleResolution();
+          appView.appInfo().resolveMethodOn(originalClazz, method.method).asSingleResolution();
       if (resolutionResult == null || !resolutionResult.isVirtualTarget()) {
         return;
       }
@@ -1286,6 +1286,10 @@
       this.delayedRootSetActionItems = delayedRootSetActionItems;
     }
 
+    public boolean noShrinking(DexReference reference) {
+      return noShrinking.containsKey(reference);
+    }
+
     public void forEachClassWithDependentItems(
         DexDefinitionSupplier definitions, Consumer<DexProgramClass> consumer) {
       for (DexReference reference : dependentNoShrinking.keySet()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 31d8074..a540236 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -382,7 +382,7 @@
     TraversalContinuation result =
         sourceClass.traverseProgramInstanceInitializers(
             method -> {
-              AbortReason reason = disallowInlining(method, targetClass.type);
+              AbortReason reason = disallowInlining(method, targetClass);
               if (reason != null) {
                 // Cannot guarantee that markForceInline() will work.
                 if (Log.ENABLED) {
@@ -1182,7 +1182,7 @@
               // Resolution would have succeeded if the method used to be in [type], or if one of
               // its super classes declared the method.
               boolean resolutionSucceededBeforeMerge =
-                  renamedMembersLense.hasMappingForSignatureInContext(holder.type, signatureInType)
+                  renamedMembersLense.hasMappingForSignatureInContext(holder, signatureInType)
                       || appInfo.lookupSuperTarget(signatureInHolder, holder) != null;
               if (resolutionSucceededBeforeMerge) {
                 deferredRenamings.mapVirtualMethodToDirectInType(
@@ -1261,7 +1261,7 @@
 
     // Returns the method that shadows the given method, or null if method is not shadowed.
     private DexEncodedMethod findMethodInTarget(DexEncodedMethod method) {
-      ResolutionResult resolutionResult = appInfo.resolveMethod(target, method.method);
+      ResolutionResult resolutionResult = appInfo.resolveMethodOn(target, method.method);
       if (!resolutionResult.isSingleResolution()) {
         // May happen in case of missing classes, or if multiple implementations were found.
         abortMerge = true;
@@ -1655,7 +1655,7 @@
     }
   }
 
-  private AbortReason disallowInlining(ProgramMethod method, DexType invocationContext) {
+  private AbortReason disallowInlining(ProgramMethod method, DexProgramClass context) {
     if (appView.options().enableInlining) {
       Code code = method.getDefinition().getCode();
       if (code.isCfCode()) {
@@ -1664,14 +1664,14 @@
             cfCode.computeInliningConstraint(
                 method,
                 appView,
-                new SingleTypeMapperGraphLense(method.getHolderType(), invocationContext),
-                invocationContext);
+                new SingleTypeMapperGraphLense(method.getHolderType(), context),
+                context);
         if (constraint == ConstraintWithTarget.NEVER) {
           return AbortReason.UNSAFE_INLINING;
         }
         // Constructors can have references beyond the root main dex classes. This can increase the
         // size of the main dex dependent classes and we should bail out.
-        if (mainDexClasses.getRoots().contains(invocationContext)
+        if (mainDexClasses.getRoots().contains(context.type)
             && MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
                 appView.appInfo(), method, mainDexClasses.getRoots())) {
           return AbortReason.MAIN_DEX_ROOT_OUTSIDE_REFERENCE;
@@ -1686,9 +1686,9 @@
   private class SingleTypeMapperGraphLense extends GraphLense {
 
     private final DexType source;
-    private final DexType target;
+    private final DexProgramClass target;
 
-    public SingleTypeMapperGraphLense(DexType source, DexType target) {
+    public SingleTypeMapperGraphLense(DexType source, DexProgramClass target) {
       this.source = source;
       this.target = target;
     }
@@ -1720,7 +1720,7 @@
 
     @Override
     public DexType lookupType(DexType type) {
-      return type == source ? target : mergedClasses.getOrDefault(type, type);
+      return type == source ? target.type : mergedClasses.getOrDefault(type, type);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 1013539..1202384 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
@@ -303,9 +304,9 @@
       return mergedClasses.getOrDefault(type, type);
     }
 
-    public boolean hasMappingForSignatureInContext(DexType context, DexMethod signature) {
+    public boolean hasMappingForSignatureInContext(DexProgramClass context, DexMethod signature) {
       Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
-          contextualVirtualToDirectMethodMaps.get(context);
+          contextualVirtualToDirectMethodMaps.get(context.type);
       if (virtualToDirectMethodMap != null) {
         return virtualToDirectMethodMap.containsKey(signature);
       }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 60b6f7d..f15c049 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -41,9 +41,9 @@
   O_MR1(27),
   P(28),
   Q(29),
-  R(30);  // Speculative, this can change.
+  R(30);
 
-  public static final AndroidApiLevel LATEST = Q;
+  public static final AndroidApiLevel LATEST = R;
 
   public static final int magicApiLevelUsedByAndroidPlatformBuild = 10000;
 
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
index d31e352..f46ac1c 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanBox.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -18,7 +18,15 @@
     return value;
   }
 
+  public void set() {
+    set(true);
+  }
+
   public void set(boolean value) {
     this.value = value;
   }
+
+  public void unset() {
+    set(false);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 99098d8..e9557fa 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -38,6 +38,7 @@
 
   public static DexVersion getDexVersion(AndroidApiLevel androidApiLevel) {
     switch (androidApiLevel) {
+      case R:
       case Q:
       case P:
         return DexVersion.V39;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 62aa29a..e92effc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -73,6 +73,7 @@
 import java.util.function.BiConsumer;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.objectweb.asm.Opcodes;
 
@@ -838,14 +839,14 @@
   }
 
   public void warningMissingTypeForDesugar(
-      Origin origin, Position position, DexType missingType, DexType contextType) {
+      Origin origin, Position position, DexType missingType, DexMethod context) {
     if (reportedMissingForDesugaring.add(missingType)) {
       reporter.warning(
           new InterfaceDesugarMissingTypeDiagnostic(
               origin,
               position,
               Reference.classFromDescriptor(missingType.toDescriptorString()),
-              Reference.classFromDescriptor(contextType.toDescriptorString()),
+              Reference.classFromDescriptor(context.holder.toDescriptorString()),
               null));
     }
   }
@@ -1186,6 +1187,8 @@
     }
 
     public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = null;
+
+    public Predicate<DexEncodedMethod> cfByteCodePassThrough = null;
   }
 
   @VisibleForTesting
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 8cdce08..d62402b 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -338,7 +338,9 @@
                 optimizeDexCodePositions(
                     method, appView, kotlinRemapper, mappedPositions, identityMapping);
               }
-            } else if (code.isCfCode() && doesContainPositions(code.asCfCode())) {
+            } else if (code.isCfCode()
+                && doesContainPositions(code.asCfCode())
+                && !appView.isCfByteCodePassThrough(method)) {
               optimizeCfCodePositions(method, kotlinRemapper, mappedPositions, appView);
             }
           }
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java b/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java
new file mode 100644
index 0000000..91553b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.google.common.base.Equivalence;
+
+public class ProgramMethodEquivalence extends Equivalence<ProgramMethod> {
+
+  private static final ProgramMethodEquivalence INSTANCE = new ProgramMethodEquivalence();
+
+  private ProgramMethodEquivalence() {}
+
+  public static ProgramMethodEquivalence get() {
+    return INSTANCE;
+  }
+
+  @Override
+  protected boolean doEquivalent(ProgramMethod method, ProgramMethod other) {
+    return method.getDefinition() == other.getDefinition();
+  }
+
+  @Override
+  protected int doHash(ProgramMethod method) {
+    return method.getReference().hashCode();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
new file mode 100644
index 0000000..5a9e280
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramField;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class ProgramFieldSet implements Iterable<ProgramField> {
+
+  private static final ProgramFieldSet EMPTY = new ProgramFieldSet(ImmutableMap.of());
+
+  private Map<DexField, ProgramField> backing;
+
+  private ProgramFieldSet(Map<DexField, ProgramField> backing) {
+    this.backing = backing;
+  }
+
+  public static ProgramFieldSet create() {
+    return new ProgramFieldSet(new IdentityHashMap<>());
+  }
+
+  public static ProgramFieldSet empty() {
+    return EMPTY;
+  }
+
+  public boolean add(ProgramField field) {
+    ProgramField existing = backing.put(field.getReference(), field);
+    assert existing == null || existing.isStructurallyEqualTo(field);
+    return existing == null;
+  }
+
+  public void addAll(Iterable<ProgramField> fields) {
+    fields.forEach(this::add);
+  }
+
+  public void addAll(ProgramFieldSet fields) {
+    backing.putAll(fields.backing);
+  }
+
+  public boolean createAndAdd(DexProgramClass clazz, DexEncodedField definition) {
+    return add(new ProgramField(clazz, definition));
+  }
+
+  public boolean contains(DexEncodedField field) {
+    return backing.containsKey(field.toReference());
+  }
+
+  public boolean contains(ProgramField field) {
+    return backing.containsKey(field.getReference());
+  }
+
+  public void clear() {
+    backing.clear();
+  }
+
+  public boolean isEmpty() {
+    return backing.isEmpty();
+  }
+
+  @Override
+  public Iterator<ProgramField> iterator() {
+    return backing.values().iterator();
+  }
+
+  public boolean remove(DexField field) {
+    ProgramField existing = backing.remove(field);
+    return existing != null;
+  }
+
+  public boolean remove(DexEncodedField field) {
+    return remove(field.toReference());
+  }
+
+  public int size() {
+    return backing.size();
+  }
+
+  public Stream<ProgramField> stream() {
+    return backing.values().stream();
+  }
+
+  public Set<DexEncodedField> toDefinitionSet() {
+    assert backing instanceof IdentityHashMap;
+    Set<DexEncodedField> definitions = Sets.newIdentityHashSet();
+    forEach(field -> definitions.add(field.getDefinition()));
+    return definitions;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index d881974..2ae20c9 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -352,18 +352,18 @@
     return descriptorList;
   }
 
-  private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
+  public static byte[] getClassAsBytes(ClassFileResourceProvider inputJar, String descriptor)
       throws Exception {
     return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
   }
 
-  private static String asmToString(byte[] clazz) {
+  public static String asmToString(byte[] clazz) {
     StringWriter stringWriter = new StringWriter();
     printAsm(new PrintWriter(stringWriter), clazz);
     return stringWriter.toString();
   }
 
-  private static void printAsm(PrintWriter pw, byte[] clazz) {
+  public static void printAsm(PrintWriter pw, byte[] clazz) {
     new ClassReader(clazz).accept(new TraceClassVisitor(null, new ASMifierSorted(), pw), 0);
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 7938666..000ffbc 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.SmaliWriter;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.jasmin.JasminBuilder;
@@ -1294,13 +1295,14 @@
     return getMethodSubject(application, className, returnType, methodName, parameters).getMethod();
   }
 
-  protected DexEncodedMethod getMethod(
+  protected ProgramMethod getMethod(
       CodeInspector inspector,
       String className,
       String returnType,
       String methodName,
       List<String> parameters) {
-    return getMethodSubject(inspector, className, returnType, methodName, parameters).getMethod();
+    return getMethodSubject(inspector, className, returnType, methodName, parameters)
+        .getProgramMethod();
   }
 
   protected static void checkInstructions(
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
index 7ea2765..3efb9b1 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationProguardCompatTest.java
@@ -15,10 +15,7 @@
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
 import org.junit.Test;
 
-/**
- * Tests that both R8 and Proguard may change the visibility of a field or method that is explicitly
- * kept.
- */
+/** Tests that Proguard may change the visibility of a field or method that is explicitly kept. */
 public class AccessRelaxationProguardCompatTest extends TestBase {
 
   private static Class<?> clazz = AccessRelaxationProguardCompatTestClass.class;
@@ -35,7 +32,7 @@
             "}")
         .allowAccessModification()
         .compile()
-        .inspect(AccessRelaxationProguardCompatTest::inspect);
+        .inspect(inspector -> inspect(inspector, true));
   }
 
   @Test
@@ -49,10 +46,10 @@
             "}")
         .allowAccessModification()
         .compile()
-        .inspect(AccessRelaxationProguardCompatTest::inspect);
+        .inspect(inspector -> inspect(inspector, false));
   }
 
-  private static void inspect(CodeInspector inspector) {
+  private static void inspect(CodeInspector inspector, boolean isR8) {
     ClassSubject classSubject = inspector.clazz(clazzWithGetter);
     assertThat(classSubject, isPresent());
 
@@ -60,7 +57,11 @@
     assertThat(fieldSubject, isPresent());
 
     // Although this field was explicitly kept, it is no longer private.
-    assertThat(fieldSubject, not(isPrivate()));
+    if (isR8) {
+      assertThat(fieldSubject, isPrivate());
+    } else {
+      assertThat(fieldSubject, not(isPrivate()));
+    }
   }
 }
 
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/PrivateKeptMembersPublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/PrivateKeptMembersPublicizerTest.java
new file mode 100644
index 0000000..940479a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/PrivateKeptMembersPublicizerTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.accessrelaxation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PrivateKeptMembersPublicizerTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public PrivateKeptMembersPublicizerTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(PrivateKeptMembersPublicizerTest.class)
+        .addKeepClassAndMembersRules(TestClass.class)
+        .allowAccessModification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(classSubject, isPresent());
+    assertTrue(classSubject.uniqueFieldWithName("greeting").isPrivate());
+    assertTrue(classSubject.uniqueMethodWithName("greet").isPrivate());
+  }
+
+  static class TestClass {
+
+    private static String greeting = "Hello world!";
+
+    public static void main(String[] args) {
+      greet(greeting);
+    }
+
+    private static void greet(String message) {
+      System.out.println(message);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index de3afb5..c27b55b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -73,7 +73,7 @@
     // }
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
     DexType typeI = buildType(I.class, appView.dexItemFactory());
     DexType typeL = buildType(L.class, appView.dexItemFactory());
     DexType typeA = buildType(A.class, appView.dexItemFactory());
@@ -114,7 +114,7 @@
     // }
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
     DexType typeI = buildType(I.class, appView.dexItemFactory());
     DexType typeL = buildType(L.class, appView.dexItemFactory());
     DexType typeA = buildType(A.class, appView.dexItemFactory());
@@ -154,7 +154,7 @@
     // }
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, J.class);
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
     DexType typeI = buildType(I.class, appView.dexItemFactory());
     DexType typeB = buildType(A.class, appView.dexItemFactory());
     DexProgramClass classI = appView.definitionForProgramType(typeI);
@@ -191,7 +191,7 @@
     // }
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, A.class);
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
     DexType typeI = buildType(I.class, appView.dexItemFactory());
     DexType typeB = buildType(A.class, appView.dexItemFactory());
     DexProgramClass classI = appView.definitionForProgramType(typeI);
@@ -230,7 +230,7 @@
     // }
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
-    ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
     DexType typeI = buildType(I.class, appView.dexItemFactory());
     DexType typeB = buildType(A.class, appView.dexItemFactory());
     DexProgramClass classI = appView.definitionForProgramType(typeI);
diff --git a/src/test/java/com/android/tools/r8/code/PassThroughTest.java b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
new file mode 100644
index 0000000..b1a9f2f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
@@ -0,0 +1,160 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import static junit.framework.Assert.assertSame;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.CfFrontendExamplesTest;
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DirectoryClassFileProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PassThroughTest extends TestBase {
+
+  private final String EXPECTED = StringUtils.lines("0", "foo", "0");
+
+  private final TestParameters parameters;
+  private final boolean keepDebug;
+
+  @Parameters(name = "{0}, keep-debug: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withCfRuntimes().build(), BooleanUtils.values());
+  }
+
+  public PassThroughTest(TestParameters parameters, boolean keepDebug) {
+    this.parameters = parameters;
+    this.keepDebug = keepDebug;
+  }
+
+  @Test
+  public void testJmv() throws Exception {
+    CodeInspector inspector =
+        testForJvm()
+            .addProgramClasses(Main.class)
+            .run(parameters.getRuntime(), Main.class)
+            .assertSuccessWithOutput(EXPECTED)
+            .inspector();
+    // Check that reading the same input is actual matches.
+    ClassFileResourceProvider original =
+        DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
+    verifyInstructionsForMainMatchingExpectation(original, true, true);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Path outputJar = temp.newFile("output.jar").toPath();
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
+        .compile()
+        .writeToZip(outputJar)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+    verifyInstructionsForMainMatchingExpectation(
+        new ArchiveClassFileProvider(outputJar), keepDebug, false);
+  }
+
+  @Test
+  public void testR8ByteCodePassThrough() throws Exception {
+    Path outputJar = temp.newFile("output.jar").toPath();
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
+        .addOptionsModification(
+            internalOptions ->
+                internalOptions.testing.cfByteCodePassThrough =
+                    method -> method.method.name.toString().equals("main"))
+        .compile()
+        .writeToZip(outputJar)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+    verifyInstructionsForMainMatchingExpectation(
+        new ArchiveClassFileProvider(outputJar), keepDebug, true);
+  }
+
+  private void verifyInstructionsForMainMatchingExpectation(
+      ClassFileResourceProvider actual, boolean checkDebug, boolean expectation) throws Exception {
+    ClassFileResourceProvider original =
+        DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
+    String descriptor = DescriptorUtils.javaTypeToDescriptor(Main.class.getTypeName());
+    byte[] expectedBytes = CfFrontendExamplesTest.getClassAsBytes(original, descriptor);
+    byte[] actualBytes = CfFrontendExamplesTest.getClassAsBytes(actual, descriptor);
+    if (!Arrays.equals(expectedBytes, actualBytes)) {
+      String expectedString = CfFrontendExamplesTest.asmToString(expectedBytes);
+      String actualString = CfFrontendExamplesTest.asmToString(actualBytes);
+      verifyInstructionsForMainMatchingExpectation(
+          getMethodInstructions(expectedString),
+          getMethodInstructions(actualString),
+          checkDebug,
+          expectation);
+    }
+  }
+
+  private String getMethodInstructions(String asm) {
+    int methodIndexStart =
+        asm.indexOf(
+            "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \"main\","
+                + " \"([Ljava/lang/String;)V\", null, null);");
+    int methodIndexEnd = asm.indexOf("}", methodIndexStart);
+    return asm.substring(methodIndexStart, methodIndexEnd);
+  }
+
+  private void verifyInstructionsForMainMatchingExpectation(
+      String originalInstructions,
+      String actualInstructions,
+      boolean checkDebug,
+      boolean expectation) {
+    if (!checkDebug) {
+      originalInstructions =
+          StringUtils.splitLines(originalInstructions).stream()
+              .filter(this::isNotDebugInstruction)
+              .map(instr -> instr + "\n")
+              .collect(Collectors.joining());
+    }
+    assertSame(expectation, actualInstructions.equals(originalInstructions));
+  }
+
+  private boolean isNotDebugInstruction(String instruction) {
+    return !(instruction.startsWith("methodVisitor.visitLocalVariable")
+        || instruction.startsWith("methodVisitor.visitLabel")
+        || instruction.startsWith("Label")
+        || instruction.startsWith("methodVisitor.visitLineNumber"));
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      int i = 0;
+      System.out.println(i);
+      int j = 0;
+      String foo = "foo";
+      // Keep the false to have R8 remove it.
+      if (false) {
+        System.out.println(foo);
+      }
+      System.out.println(foo);
+      System.out.println(j);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
index 5dac547..e9d9d79 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
@@ -6,18 +6,16 @@
 
 import static org.hamcrest.core.StringContains.containsString;
 
+import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 
 @RunWith(Parameterized.class)
 public class ApiLevelBackportsTest extends TestBase {
@@ -26,6 +24,8 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
+    // NOTE: Most of the 'run' invocations below work only because the static configured APIs do not
+    // give rise to DEX file versions larger than what can be accepted by VM 9.0.0.
     return getTestParameters().withDexRuntimesStartingFromIncluding(Version.V9_0_0).build();
   }
 
@@ -36,22 +36,49 @@
   @Test
   public void backportSucceedsOnSupportedApiLevel() throws Exception {
     testForD8()
-        .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt())
+        .addProgramClassFileData(transformTestMathMultiplyExactLongInt())
         .setMinApi(AndroidApiLevel.B)
-        .run(parameters.getRuntime(), "Test")
+        .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
         .assertSuccessWithOutputLines("4");
   }
 
   @Test
-  public void warningForNonPlatformBuild() throws Exception {
+  public void backportOfNonPresentMethodOnLatest() throws Exception {
     testForD8()
-        .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt())
-        .setMinApi(30)
+        .addProgramClassFileData(transformTestMathMultiplyExactLongInt())
+        .setMinApi(AndroidApiLevel.LATEST)
+        .compile()
+        .assertNoMessages()
+        .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
+        .assertSuccessWithOutputLines("4");
+  }
+
+  @Test
+  public void backportOfPresentMethodOnLatest() throws Exception {
+    D8TestRunResult result =
+        testForD8()
+            .addProgramClassFileData(transformTestListOf())
+            .setMinApi(AndroidApiLevel.LATEST)
+            .compile()
+            .assertNoMessages()
+            .run(parameters.getRuntime(), TestListOf.class);
+    if (runtimeHasListOf()) {
+      result.assertSuccessWithOutputLines("0");
+    } else {
+      result.assertFailureWithErrorThatMatches(
+          containsString("java.lang.NoSuchMethodError: No static method of()Ljava/util/List;"));
+    }
+  }
+
+  @Test
+  public void warningForFutureNonPlatformBuild() throws Exception {
+    testForD8()
+        .addProgramClassFileData(transformTestMathMultiplyExactLongInt())
+        .setMinApi(AndroidApiLevel.LATEST.getLevel() + 1)
         .compile()
         .assertOnlyWarnings()
-        .assertWarningMessageThatMatches(
-            containsString("An API level of 30 is not supported by this compiler"))
-        .run(parameters.getRuntime(), "Test")
+        .assertWarningMessageThatMatches(containsString("is not supported by this compiler"))
+        .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
         .assertFailureWithErrorThatMatches(
             containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J"));
   }
@@ -59,70 +86,78 @@
   @Test
   public void noWarningForPlatformBuild() throws Exception {
     testForD8()
-        .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt())
+        .addProgramClassFileData(transformTestMathMultiplyExactLongInt())
         .setMinApi(AndroidApiLevel.magicApiLevelUsedByAndroidPlatformBuild)
-        .run(parameters.getRuntime(), "Test")
+        .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
         .assertFailureWithErrorThatMatches(
             containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J"));
   }
 
-  static class Dump implements Opcodes {
+  // Test class for using: List List.of()
+  // Introduced in Android R.
 
-    // Code for:
-    //
-    // class Test {
-    //   public static void main(String[] args) {
-    //     // Call Math.multiplyExact(long, int), which is not in Android Q.
-    //     System.out.println(Math.multiplyExact(2L, 2));
-    //   }
-    // }
-    //
-    static byte[] mainWithMathMultiplyExactLongInt() {
+  boolean runtimeHasListOf() {
+    return parameters
+        .getRuntime()
+        .asDex()
+        .getMinApiLevel()
+        .isGreaterThanOrEqualTo(AndroidApiLevel.R);
+  }
 
-      ClassWriter classWriter = new ClassWriter(0);
-      MethodVisitor methodVisitor;
+  byte[] transformTestListOf() throws Exception {
+    return transformer(TestListOf.class)
+        .transformMethodInsnInMethod(
+            "main",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("List_of")) {
+                visitor.visitMethodInsn(opcode, "java/util/List", "of", descriptor, isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .transform();
+  }
 
-      classWriter.visit(V1_8, ACC_SUPER, "Test", null, "java/lang/Object", null);
+  static class TestListOf {
+    public static List List_of() {
+      throw null;
+    }
 
-      classWriter.visitSource("Test.java", null);
+    public static void main(String[] args) {
+      System.out.println(List_of().size());
+    }
+  }
 
-      {
-        methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
-        methodVisitor.visitCode();
-        Label label0 = new Label();
-        methodVisitor.visitLabel(label0);
-        methodVisitor.visitLineNumber(1, label0);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
-        methodVisitor.visitInsn(RETURN);
-        methodVisitor.visitMaxs(1, 1);
-        methodVisitor.visitEnd();
-      }
-      {
-        methodVisitor =
-            classWriter.visitMethod(
-                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
-        methodVisitor.visitCode();
-        Label label0 = new Label();
-        methodVisitor.visitLabel(label0);
-        methodVisitor.visitLineNumber(3, label0);
-        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
-        methodVisitor.visitLdcInsn(Long.valueOf(2L));
-        methodVisitor.visitLdcInsn(Integer.valueOf(2));
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC, "java/lang/Math", "multiplyExact", "(JI)J", false);
-        methodVisitor.visitMethodInsn(
-            INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false);
-        Label label1 = new Label();
-        methodVisitor.visitLabel(label1);
-        methodVisitor.visitLineNumber(4, label1);
-        methodVisitor.visitInsn(RETURN);
-        methodVisitor.visitMaxs(5, 1);
-        methodVisitor.visitEnd();
-      }
-      classWriter.visitEnd();
+  // Test class for the method: long Math.multiplyExact(long, int)
+  // Not present on any currently known Android platforms.
 
-      return classWriter.toByteArray();
+  boolean runtimeHasMathMultiplyExactLongInt() {
+    // NOTE: This may change with a future release.
+    return false;
+  }
+
+  byte[] transformTestMathMultiplyExactLongInt() throws Exception {
+    return transformer(TestMathMultiplyExactLongInt.class)
+        .transformMethodInsnInMethod(
+            "main",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (name.equals("Math_multiplyExact")) {
+                visitor.visitMethodInsn(
+                    opcode, "java/lang/Math", "multiplyExact", descriptor, isInterface);
+              } else {
+                visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .transform();
+  }
+
+  static class TestMathMultiplyExactLongInt {
+    public static long Math_multiplyExact(long l, int i) {
+      throw null;
+    }
+
+    public static void main(String[] args) {
+      System.out.println(Math_multiplyExact(2L, 2));
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
new file mode 100644
index 0000000..8dd0d27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugaredLibraryWarningTest extends DesugaredLibraryTestBase {
+
+  private static final String FUNCTION_KEEP =
+      "-keep class j$.util.function.Function$-CC {\n"
+          + "    j$.util.function.Function $default$compose(j$.util.function.Function,"
+          + " j$.util.function.Function);\n"
+          + "    j$.util.function.Function $default$andThen(j$.util.function.Function,"
+          + " j$.util.function.Function);\n"
+          + "}\n"
+          + "-keep class j$.util.function.Function { *; }";
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameterized.Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+  }
+
+  public DesugaredLibraryWarningTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testDesugaredLibraryContent() throws Exception {
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
+    L8Command.Builder l8Builder =
+        L8Command.builder(diagnosticsHandler)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+            .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setMinApiLevel(parameters.getApiLevel().getLevel())
+            .setOutput(desugaredLib, OutputMode.DexIndexed);
+    if (shrinkDesugaredLibrary) {
+      l8Builder.addProguardConfiguration(
+          Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
+    }
+    ToolHelper.runL8(l8Builder.build(), options -> {});
+    diagnosticsHandler.assertNoMessages();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index 945f3c2..71d5ffb 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -127,13 +128,15 @@
         .addDexProgramData(Files.toByteArray(originalDexFile.toFile()), Origin.unknown())
         .build();
     CodeInspector inspector = new CodeInspector(application);
-    DexEncodedMethod method = getMethod(
-        inspector,
-        "android.databinding.DataBinderMapperImpl",
-        "android.databinding.ViewDataBinding",
-        "getDataBinder",
-        ImmutableList.of("android.databinding.DataBindingComponent", "android.view.View", "int"));
-    Instruction[] instructions = method.getCode().asDexCode().instructions;
+    ProgramMethod method =
+        getMethod(
+            inspector,
+            "android.databinding.DataBinderMapperImpl",
+            "android.databinding.ViewDataBinding",
+            "getDataBinder",
+            ImmutableList.of(
+                "android.databinding.DataBindingComponent", "android.view.View", "int"));
+    Instruction[] instructions = method.getDefinition().getCode().asDexCode().instructions;
     assertEquals(0, countJumboStrings(instructions));
     assertEquals(1, countSimpleNops(instructions));
 
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 8524677..9c86160 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -80,12 +80,11 @@
     AndroidApp application = buildApplication(builder);
     AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application);
     CodeInspector inspector = new CodeInspector(appInfo.app());
-    DexEncodedMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x",
-        ImmutableList.of());
+    ProgramMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of());
     assertFalse(
-        appInfo.resolveMethod(method.holder(), method.method).getSingleTarget().isVirtualMethod());
-    assertNull(appInfo.lookupDirectTarget(method.method, method.holder()));
-    assertNotNull(appInfo.lookupStaticTarget(method.method, method.holder()));
+        appInfo.resolveMethodOnClass(method.getReference()).getSingleTarget().isVirtualMethod());
+    assertNull(appInfo.lookupDirectTarget(method.getReference(), method));
+    assertNotNull(appInfo.lookupStaticTarget(method.getReference(), method));
 
     if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) {
       // Dalvik rejects at verification time instead of producing the
@@ -152,32 +151,35 @@
     AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application);
     CodeInspector inspector = new CodeInspector(appInfo.app());
 
-    DexMethod methodXOnTestSuper =
-        getMethod(inspector, "TestSuper", "int", "x", ImmutableList.of()).method;
-    DexMethod methodYOnTest =
-        getMethod(inspector, "Test", "int", "y", ImmutableList.of()).method;
+    ProgramMethod methodXOnTestSuper =
+        getMethod(inspector, "TestSuper", "int", "x", ImmutableList.of());
+    ProgramMethod methodYOnTest = getMethod(inspector, "Test", "int", "y", ImmutableList.of());
 
-    DexType classTestSuper = methodXOnTestSuper.holder;
-    DexType classTest = methodYOnTest.holder;
-    DexProto methodXProto = methodXOnTestSuper.proto;
-    DexString methodXName = methodXOnTestSuper.name;
-    DexMethod methodXOnTest =
+    DexType classTestSuper = methodXOnTestSuper.getHolderType();
+    DexType classTest = methodYOnTest.getHolderType();
+    DexProto methodXProto = methodXOnTestSuper.getReference().proto;
+    DexString methodXName = methodXOnTestSuper.getReference().name;
+    DexMethod methodXOnTestReference =
         appInfo.dexItemFactory().createMethod(classTest, methodXProto, methodXName);
 
     assertFalse(
         appInfo
-            .resolveMethod(classTestSuper, methodXOnTestSuper)
+            .resolveMethodOnClass(methodXOnTestSuper.getReference(), classTestSuper)
             .getSingleTarget()
             .isVirtualMethod());
-    assertNull(appInfo.resolveMethod(classTest, methodXOnTestSuper).getSingleTarget());
-    assertNull(appInfo.resolveMethod(classTest, methodXOnTest).getSingleTarget());
+    assertNull(
+        appInfo
+            .resolveMethodOnClass(methodXOnTestSuper.getReference(), classTest)
+            .getSingleTarget());
+    assertNull(appInfo.resolveMethodOnClass(methodXOnTestReference, classTest).getSingleTarget());
 
-    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper, methodXOnTestSuper.holder));
-    assertNull(appInfo.lookupDirectTarget(methodXOnTest, methodXOnTest.holder));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestReference, methodYOnTest));
 
-    assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper, methodXOnTestSuper.holder));
+    assertNotNull(
+        appInfo.lookupStaticTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper));
     // Accessing a private target on a different type will fail resolution outright.
-    assertNull(appInfo.lookupStaticTarget(methodXOnTest, methodXOnTest.holder));
+    assertNull(appInfo.lookupStaticTarget(methodXOnTestReference, methodYOnTest));
 
     assertEquals("OK", runArt(application));
   }
@@ -211,8 +213,7 @@
     DexField aFieldOnInterface = factory
         .createField(factory.createType("LInterface;"), factory.intType, "aField");
 
-    assertEquals(aFieldOnInterface,
-        appInfo.lookupStaticTarget(aFieldOnSubClass.holder, aFieldOnSubClass).field);
+    assertEquals(aFieldOnInterface, appInfo.lookupStaticTarget(aFieldOnSubClass).field);
 
     assertEquals("42", runArt(application));
 
@@ -244,13 +245,13 @@
     DexType i3 = factory.createType("L" + pkg + "/I3;");
     DexType i4 = factory.createType("L" + pkg + "/I4;");
     DexType c0 = factory.createType("L" + pkg + "/C0;");
-    DexType c1 = factory.createType("L" + pkg + "/C1;");
-    DexType c2 = factory.createType("L" + pkg + "/C2;");
+    DexProgramClass c1 = appInfo.definitionForProgramType(factory.createType("L" + pkg + "/C1;"));
+    DexProgramClass c2 = appInfo.definitionForProgramType(factory.createType("L" + pkg + "/C2;"));
 
     DexProto mProto = factory.createProto(factory.intType);
     DexString m = factory.createString("m");
     DexMethod mOnC0 = factory.createMethod(c0, mProto, m);
-    DexMethod mOnC1 = factory.createMethod(c1, mProto, m);
+    DexMethod mOnC1 = factory.createMethod(c1.type, mProto, m);
     DexMethod mOnI0 = factory.createMethod(i0, mProto, m);
     DexMethod mOnI1 = factory.createMethod(i1, mProto, m);
     DexMethod mOnI2 = factory.createMethod(i2, mProto, m);
diff --git a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
index 2c06bd8..64b3fc0 100644
--- a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
@@ -27,7 +27,7 @@
         ImmutableList.of(BASE + DEPLOY_JAR));
     assertEquals(0, filterKotlinMetadata(handler.warnings).count());
     // TODO(b/155536535): We find bad descriptors. See if we can still resolve them.
-    assertEquals(2, filterKotlinMetadata(handler.infos).count());
+    assertEquals(0, filterKotlinMetadata(handler.infos).count());
   }
 
   private Stream<Diagnostic> filterKotlinMetadata(List<Diagnostic> warnings) {
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index ccbfd3a..5048226 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -72,10 +72,11 @@
   private void testVirtualLookup(DexProgramClass clazz, DexEncodedMethod method) {
     // Check lookup will produce the same result.
     DexMethod id = method.method;
-    assertEquals(appInfo().resolveMethod(id.holder, method.method).getSingleTarget(), method);
+    assertEquals(
+        appInfo().resolveMethodOnClass(method.method, id.holder).getSingleTarget(), method);
 
     // Check lookup targets with include method.
-    ResolutionResult resolutionResult = appInfo().resolveMethodOnClass(clazz, method.method);
+    ResolutionResult resolutionResult = appInfo().resolveMethodOnClass(method.method, clazz);
     AppInfoWithLiveness appInfo = null; // TODO(b/154881041): Remove or compute liveness.
     LookupResult lookupResult =
         resolutionResult.lookupVirtualDispatchTargets(
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 2b3e44d..7104c9a 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -8,7 +8,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -61,8 +61,7 @@
 
     InternalOptions options = new InternalOptions();
     DexApplication application = buildApplication(builder, options);
-    AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForD8(new AppInfoWithClassHierarchy(application), options);
+    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
 
     // Return the processed method for inspection.
     MethodSubject methodSubject = getMethodSubject(application, signature);
@@ -178,8 +177,7 @@
 
     InternalOptions options = new InternalOptions();
     DexApplication application = buildApplication(builder, options);
-    AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForD8(new AppInfoWithClassHierarchy(application), options);
+    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
 
     // Return the processed method for inspection.
     MethodSubject methodSubject = getMethodSubject(application, signature);
@@ -303,8 +301,7 @@
 
     InternalOptions options = new InternalOptions();
     DexApplication application = buildApplication(builder, options);
-    AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForD8(new AppInfoWithClassHierarchy(application), options);
+    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
 
     // Return the processed method for inspection.
     MethodSubject methodSubject = getMethodSubject(application, signature);
@@ -427,8 +424,7 @@
 
     InternalOptions options = new InternalOptions();
     DexApplication application = buildApplication(builder, options);
-    AppView<AppInfoWithClassHierarchy> appView =
-        AppView.createForD8(new AppInfoWithClassHierarchy(application), options);
+    AppView<?> appView = AppView.createForD8(new AppInfo(application), options);
 
     // Return the processed method for inspection.
     MethodSubject methodSubject = getMethodSubject(application, signature);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisForNameReflectionTest.java b/src/test/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisForNameReflectionTest.java
index c337379..9362432 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisForNameReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysisForNameReflectionTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.AnalysisTestBase;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -176,14 +177,14 @@
         AppView<?> appView,
         EscapeAnalysis escapeAnalysis,
         Instruction escapeRoute,
-        DexMethod context) {
+        ProgramMethod context) {
       if (escapeRoute.isReturn() || escapeRoute.isThrow() || escapeRoute.isStaticPut()) {
         return false;
       }
       if (escapeRoute.isInvokeMethod()) {
         DexMethod invokedMethod = escapeRoute.asInvokeMethod().getInvokedMethod();
         // Heuristic: if the call target has the same method name, it could be still local.
-        if (invokedMethod.name == context.name) {
+        if (invokedMethod.name == context.getReference().name) {
           return true;
         }
         // It's not legitimate during testing, except for recursion calls.
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 6177bf3..5a53fb5 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -80,7 +80,7 @@
 
   @Test
   public void testOptimizationInfo() throws Exception {
-    AppView<AppInfoWithClassHierarchy> appView = buildApp();
+    AppView<? extends AppInfoWithClassHierarchy> appView = buildApp();
     OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
     FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
     FieldAccessAnalysis fieldAccessAnalysis =
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 2590f1b..1bee09a 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -11,6 +11,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Argument;
@@ -45,7 +46,7 @@
       boolean npeCaught,
       BiConsumer<AppView<?>, IRCode> inspector)
       throws Exception {
-    AppView<?> appView = build(mainClass);
+    AppView<? extends AppInfoWithClassHierarchy> appView = build(mainClass);
     CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
     MethodSubject fooSubject = codeInspector.clazz(mainClass.getName()).method(signature);
     IRCode irCode = fooSubject.buildIR();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 50742b5..3d28b08 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.InstancePut;
@@ -32,7 +33,7 @@
       int expectedNumberOfNonNull,
       Consumer<IRCode> testAugmentedIRCode)
       throws Exception {
-    AppView<?> appView = build(testClass);
+    AppView<? extends AppInfoWithClassHierarchy> appView = build(testClass);
     CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
     MethodSubject fooSubject = codeInspector.clazz(testClass.getName()).method(signature);
     IRCode code = fooSubject.buildIR();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 28ab813..4820221 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -16,14 +16,15 @@
 
 public abstract class NonNullTrackerTestBase extends TestBase {
 
-  protected AppView<?> build(Class<?> mainClass) throws Exception {
+  protected AppView<? extends AppInfoWithClassHierarchy> build(Class<?> mainClass)
+      throws Exception {
     Timing timing = Timing.empty();
     AndroidApp app = buildAndroidApp(ToolHelper.getClassAsBytes(mainClass));
     InternalOptions options = new InternalOptions();
     DirectMappedDexApplication dexApplication =
         new ApplicationReader(app, options, timing).read().toDirect();
-    AppView<?> appView =
-        AppView.createForD8(new AppInfoWithClassHierarchy(dexApplication), options);
+    AppView<? extends AppInfoWithClassHierarchy> appView =
+        AppView.createForR8(new AppInfoWithClassHierarchy(dexApplication), options);
     appView.setAppServices(AppServices.builder(appView).build());
     return appView;
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index 466b6fd..14d7e6b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -31,16 +31,6 @@
     }
   }
 
-  private void testD8(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
-    CodeInspector codeInspector =
-        testForD8()
-            .addProgramClasses(testClass)
-            .addOptionsModification(options -> options.enableNonNullTracking = true)
-            .compile()
-            .inspector();
-    verifyAbsenceOfIf(codeInspector, testClass, signatures);
-  }
-
   private void testR8(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
     CodeInspector codeInspector =
         testForR8(Backend.DEX)
@@ -57,7 +47,6 @@
         new MethodSignature("foo", "int", new String[]{"java.lang.String"});
     MethodSignature bar =
         new MethodSignature("bar", "int", new String[]{"java.lang.String"});
-    testD8(NonNullAfterInvoke.class, ImmutableList.of(foo, bar));
     testR8(NonNullAfterInvoke.class, ImmutableList.of(foo, bar));
   }
 
@@ -67,7 +56,6 @@
         new MethodSignature("foo", "int", new String[]{"java.lang.String[]"});
     MethodSignature bar =
         new MethodSignature("bar", "int", new String[]{"java.lang.String[]"});
-    testD8(NonNullAfterArrayAccess.class, ImmutableList.of(foo, bar));
     testR8(NonNullAfterArrayAccess.class, ImmutableList.of(foo, bar));
   }
 
@@ -79,7 +67,6 @@
         new String[]{FieldAccessTest.class.getCanonicalName()});
     MethodSignature foo2 = new MethodSignature("foo2", "int",
         new String[]{FieldAccessTest.class.getCanonicalName()});
-    testD8(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
     testR8(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningWithImpreciseReceiverTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningWithImpreciseReceiverTypeTest.java
new file mode 100644
index 0000000..cfdfcda
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningWithImpreciseReceiverTypeTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ClassInliningWithImpreciseReceiverTypeTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ClassInliningWithImpreciseReceiverTypeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ClassInliningWithImpreciseReceiverTypeTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("C1", "C2");
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      // After force inlining asB() and build(), the class inliner will attempt to force inline
+      // internalBuild(), but the receiver of that invoke is defined by a check-cast instruction
+      // that casts the receiver to B.
+      System.out.println(new C1().asB().build());
+      System.out.println(new C2().asB().build());
+    }
+  }
+
+  @NeverMerge
+  abstract static class A {
+
+    @NeverInline
+    B asB() {
+      return (B) this;
+    }
+
+    @NeverInline
+    String build() {
+      return internalBuild();
+    }
+
+    abstract String internalBuild();
+  }
+
+  abstract static class B extends A {}
+
+  static class C1 extends B {
+
+    @NeverInline
+    String internalBuild() {
+      return System.currentTimeMillis() > 0 ? "C1" : null;
+    }
+  }
+
+  static class C2 extends B {
+
+    @NeverInline
+    String internalBuild() {
+      return System.currentTimeMillis() > 0 ? "C2" : null;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index f743bf2..c4dbbc4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -106,31 +106,6 @@
   }
 
   @Test
-  public void testD8() throws Exception {
-    assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
-
-    TestRunResult result =
-        testForD8()
-            .debug()
-            .addProgramClassesAndInnerClasses(MAIN)
-            .setMinApi(parameters.getRuntime())
-            .addOptionsModification(options -> options.enableNonNullTracking = true)
-            .run(parameters.getRuntime(), MAIN)
-            .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, false, false);
-
-    result =
-        testForD8()
-            .release()
-            .addProgramClassesAndInnerClasses(MAIN)
-            .setMinApi(parameters.getRuntime())
-            .addOptionsModification(options -> options.enableNonNullTracking = true)
-            .run(parameters.getRuntime(), MAIN)
-            .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, false, true);
-  }
-
-  @Test
   public void testR8() throws Exception {
     TestRunResult result =
         testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
new file mode 100644
index 0000000..bc0f2a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteBoxedTypesTest extends KotlinMetadataTestBase {
+
+  private final String EXPECTED =
+      StringUtils.lines("false", "0", "a", "0.042", "0.42", "42", "442", "1", "2", "42", "42");
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteBoxedTypesTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>();
+  private final TestParameters parameters;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String baseLibFolder = PKG_PREFIX + "/box_primitives_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path baseLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib"))
+              .compile();
+      libJars.put(targetVersion, baseLibJar);
+    }
+  }
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path libJar = libJars.get(targetVersion);
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void smokeTestReflection() throws Exception {
+    Path libJar = libJars.get(targetVersion);
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main_reflect"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addVmArguments("-ea")
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadataForLib() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(libJars.get(targetVersion))
+            .addKeepAllClassesRule()
+            .addKeepAttributes(
+                ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+                ProguardKeepAttributes.SIGNATURE,
+                ProguardKeepAttributes.INNER_CLASSES,
+                ProguardKeepAttributes.ENCLOSING_METHOD)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+    Path main =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
+        .addClasspath(main)
+        .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private void inspect(CodeInspector inspector) throws IOException, ExecutionException {
+    // Since this has a keep-all classes rule, we should just assert that the meta-data is equal to
+    // the original one.
+    CodeInspector stdLibInspector = new CodeInspector(libJars.get(targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      // We cannot assert equality of the data since it may be ordered differently. Instead we use
+      // the KotlinMetadataWriter.
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      assertEquals(expected, actual);
+    }
+  }
+
+  @Test
+  public void testMetadataForReflect() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(libJars.get(targetVersion))
+            .addKeepAllClassesRule()
+            .addKeepAttributes(
+                ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+                ProguardKeepAttributes.SIGNATURE,
+                ProguardKeepAttributes.INNER_CLASSES,
+                ProguardKeepAttributes.ENCLOSING_METHOD)
+            .compile()
+            .writeToZip();
+    Path main =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main_reflect"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addVmArguments("-ea")
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
+        .addClasspath(main)
+        .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main.kt
new file mode 100644
index 0000000..2a7cb0c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main.kt
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.box_primitives_app
+
+import com.android.tools.r8.kotlin.metadata.box_primitives_lib.Test
+
+fun main() {
+  var test = Test()
+  test.testBoolean.add(test.boolean)
+  println(test.getFirstBoolean(test.testBoolean))
+  test.testByte.add(test.byte)
+  println(test.getFirstByte(test.testByte))
+  test.testChar.add(test.char)
+  println(test.getFirstChar(test.testChar))
+  test.testDouble.add(test.double)
+  println(test.getFirstDouble(test.testDouble))
+  test.testFloat.add(test.float)
+  println(test.getFirstFloat(test.testFloat))
+  test.testInt.add(test.int)
+  println(test.getFirstInt(test.testInt))
+  test.testLong.add(test.long)
+  println(test.getFirstLong(test.testLong))
+  test.testShort.add(test.short)
+  println(test.getFirstShort(test.testShort))
+  test.testNumber.add(test.number)
+  println(test.getFirstNumber(test.testNumber))
+  test.functionWithUnit { println(it) }
+  test.functionWithVoid { println(it); null }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main_reflect.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main_reflect.kt
new file mode 100644
index 0000000..be89c90
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_app/main_reflect.kt
@@ -0,0 +1,72 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.box_primitives_app
+
+import com.android.tools.r8.kotlin.metadata.box_primitives_lib.Test
+
+fun trivialTypeAssertionsForProperties() {
+  val test = Test()
+  assert(test::boolean.returnType.classifier.toString().equals("class kotlin.Boolean"))
+  assert(test::byte.returnType.classifier.toString().equals("class kotlin.Byte"))
+  assert(test::char.returnType.classifier.toString().equals("class kotlin.Char"))
+  assert(test::double.returnType.classifier.toString().equals("class kotlin.Double"))
+  assert(test::float.returnType.classifier.toString().equals("class kotlin.Float"))
+  assert(test::int.returnType.classifier.toString().equals("class kotlin.Int"))
+  assert(test::long.returnType.classifier.toString().equals("class kotlin.Long"))
+  assert(test::short.returnType.classifier.toString().equals("class kotlin.Short"))
+  assert(test::number.returnType.classifier.toString().equals("class kotlin.Number"))
+}
+
+fun trivialTypeAssertionsForFunctions() {
+  val test = Test()
+  assert(test::getFirstBoolean.parameters.size == 1)
+  assert(test::getFirstBoolean.parameters.get(0).name == "l")
+  assert(test::getFirstBoolean
+           .parameters.get(0).type.classifier.toString() == "class kotlin.collections.List")
+  assert(test::getFirstBoolean.parameters.get(0).type.arguments.size == 1)
+  assert(test::getFirstBoolean
+           .parameters.get(0).type.arguments.get(0).type!!.classifier.toString().equals(
+                test::boolean.returnType.classifier.toString()))
+}
+
+fun runReflective() {
+  val test = Test()
+  // Test boxed types
+  test::boolean.set(false)
+  test::testBoolean.get().add(test::boolean.get())
+  println(test::getFirstBoolean.call(test::testBoolean.get()))
+  test::byte.set(0)
+  test::testByte.get().add(test::byte.get())
+  println(test::getFirstByte.call(test::testByte.get()))
+  test::char.set('a')
+  test::testChar.get().add(test::char.get())
+  println(test::getFirstChar.call(test::testChar.get()))
+  test::double.set(0.042)
+  test::testDouble.get().add(test::double.get())
+  println(test::getFirstDouble.call(test::testDouble.get()))
+  test::float.set(0.42F)
+  test::testFloat.get().add(test::float.get())
+  println(test::getFirstFloat.call(test::testFloat.get()))
+  test::int.set(42)
+  test::testInt.get().add(test::int.get())
+  println(test::getFirstInt.call(test::testInt.get()))
+  test::long.set(442)
+  test::testLong.get().add(test::long.get())
+  println(test::getFirstLong.call(test::testLong.get()))
+  test::short.set(1)
+  test::testShort.get().add(test::short.get())
+  println(test::getFirstShort.call(test::testShort.get()))
+  test::number.set(2)
+  test::testNumber.get().add(test::number.get())
+  println(test::getFirstNumber.call(test::testNumber.get()))
+  test::functionWithUnit.call({ i: Int -> println(i) })
+  test::functionWithVoid.call({ i: Int -> println(i); null })
+}
+
+fun main() {
+  trivialTypeAssertionsForProperties()
+  trivialTypeAssertionsForFunctions()
+  runReflective()
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_lib/lib.kt
new file mode 100644
index 0000000..7193b62
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/box_primitives_lib/lib.kt
@@ -0,0 +1,41 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.box_primitives_lib
+
+class Test {
+
+  val testReadOnly : List<Boolean> = listOf()
+
+  var boolean : Boolean = false
+  var testBoolean : MutableList<Boolean> = mutableListOf()
+  var byte : Byte = 0
+  var testByte : MutableList<Byte> = mutableListOf()
+  var char : Char = 'a'
+  var testChar : MutableList<Char> = mutableListOf()
+  var double : Double = 0.042
+  var testDouble : MutableList<Double> = mutableListOf()
+  var float : Float = 0.42F
+  var testFloat : MutableList<Float> = mutableListOf()
+  var int : Int = 42
+  var testInt : MutableList<Int> = mutableListOf()
+  var long : Long = 442
+  var testLong : MutableList<Long> = mutableListOf()
+  var short : Short = 1
+  var testShort : MutableList<Short> = mutableListOf()
+  var number : Number = 2
+  var testNumber : MutableList<Number> = mutableListOf()
+
+  fun getFirstBoolean(l : List<Boolean>) : Boolean = l.get(0)
+  fun getFirstByte(l : List<Byte>) : Byte = l.get(0)
+  fun getFirstChar(l : List<Char>) : Char = l.get(0)
+  fun getFirstDouble(l : List<Double>) : Double = l.get(0)
+  fun getFirstFloat(l : List<Float>) : Float = l.get(0)
+  fun getFirstInt(l : List<Int>) : Int = l.get(0)
+  fun getFirstLong(l : List<Long>) : Long = l.get(0)
+  fun getFirstShort(l : List<Short>) : Short = l.get(0)
+  fun getFirstNumber(l : List<Number>) : Number = l.get(0)
+  fun functionWithUnit(consumer : (Int) -> Unit) = consumer(42)
+  fun functionWithVoid(consumer : (Int) -> Void?) = consumer(42)
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
index 02cb64e..4ad2bed 100644
--- a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
@@ -51,20 +51,18 @@
         };
     DexEncodedMethod langObjectNotifyMethod =
         appInfo
-            .resolveMethod(
-                fooType,
+            .resolveMethodOnClass(
                 factory.createMethod(fooType, factory.createProto(factory.voidType), "notify"))
             .getSingleTarget();
     for (DexType arrType : arrayTypes) {
       assertNull(
           appInfo
-              .resolveMethod(
-                  arrType, factory.createMethod(arrType, factory.createProto(arrType), "clone"))
+              .resolveMethodOnClass(
+                  factory.createMethod(arrType, factory.createProto(arrType), "clone"))
               .getSingleTarget());
       DexEncodedMethod target =
           appInfo
-              .resolveMethod(
-                  arrType,
+              .resolveMethodOnClass(
                   factory.createMethod(arrType, factory.createProto(factory.voidType), "notify"))
               .getSingleTarget();
       assertEquals(langObjectNotifyMethod, target);
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
index bade483..8f4ec7b 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -58,7 +58,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(Base.class, "collect", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     assertTrue(resolutionResult.isSingleResolution());
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 7bd9cc9..c119db0 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.resolution.singletarget.Main;
 import com.android.tools.r8.resolution.singletarget.one.AbstractSubClass;
@@ -32,9 +33,8 @@
 import com.android.tools.r8.resolution.singletarget.two.OtherSubSubClassTwo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
+import com.google.common.collect.Sets;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.junit.Assert;
@@ -76,9 +76,9 @@
 
   public SingleTargetLookupTest(
       String methodName,
-      Class invokeReceiver,
-      Class singleTargetHolderOrNull,
-      List<Class> virtualTargetHolders) {
+      Class<?> invokeReceiver,
+      Class<?> singleTargetHolderOrNull,
+      List<Class<?>> virtualTargetHolders) {
     this.methodName = methodName;
     this.invokeReceiver = invokeReceiver;
     this.singleTargetHolderOrNull = singleTargetHolderOrNull;
@@ -166,22 +166,24 @@
         });
   }
 
-  private static DexType toType(Class clazz, AppInfo appInfo) {
+  private static DexType toType(Class<?> clazz, AppInfo appInfo) {
     return buildType(clazz, appInfo.dexItemFactory());
   }
 
   private final String methodName;
-  private final Class invokeReceiver;
-  private final Class singleTargetHolderOrNull;
-  private final List<Class> virtualTargetHolders;
+  private final Class<?> invokeReceiver;
+  private final Class<?> singleTargetHolderOrNull;
+  private final List<Class<?>> virtualTargetHolders;
 
   @Test
   public void lookupSingleTarget() {
-    DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
-    Assert.assertNotNull(
-        appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
+    DexMethod reference =
+        buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
+    ProgramMethod context =
+        appInfo.definitionForProgramType(reference.holder).getProgramDefaultInitializer();
+    Assert.assertNotNull(appInfo.resolveMethodOnClass(reference).getSingleTarget());
     DexEncodedMethod singleVirtualTarget =
-        appInfo.lookupSingleVirtualTarget(method, method.holder, false);
+        appInfo.lookupSingleVirtualTarget(reference, context, false);
     if (singleTargetHolderOrNull == null) {
       Assert.assertNull(singleVirtualTarget);
     } else {
@@ -191,11 +193,10 @@
   }
 
   @Test
-  public void lookupVirtualTargets() throws IOException {
+  public void lookupVirtualTargets() {
     DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
-    Assert.assertNotNull(
-        appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    Assert.assertNotNull(appInfo.resolveMethodOnClass(method).getSingleTarget());
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     if (resolutionResult.isVirtualTarget()) {
       LookupResult lookupResult =
           resolutionResult.lookupVirtualDispatchTargets(
@@ -203,7 +204,7 @@
               appInfo);
       assertTrue(lookupResult.isLookupResultSuccess());
       assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
-      Set<DexType> targetHolders = new HashSet<>();
+      Set<DexType> targetHolders = Sets.newIdentityHashSet();
       lookupResult
           .asLookupResultSuccess()
           .forEach(
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
index 28b468e..c0996fe 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
@@ -80,7 +80,7 @@
 
   @Test
   public void resolveTarget() {
-    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
     assertTrue(resolutionResult instanceof IllegalAccessOrNoSuchMethodResult);
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
index 1da4ff7..5da5b2d 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
@@ -101,7 +101,7 @@
 
   @Test
   public void testResolution() {
-    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
     assertTrue(resolutionResult.isFailedResolution());
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index eb40786..5b08461 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -108,9 +110,9 @@
   }
 
   private final TestParameters parameters;
-  private final DexMethod methodOnA = buildMethod(A.class, "f");
-  private final DexMethod methodOnB = buildMethod(B.class, "f");
-  private final DexMethod methodOnC = buildMethod(C.class, "f");
+  private final DexMethod methodOnAReference = buildMethod(A.class, "f");
+  private final DexMethod methodOnBReference = buildMethod(B.class, "f");
+  private final DexMethod methodOnCReference = buildMethod(C.class, "f");
 
   public VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest(TestParameters parameters) {
     this.parameters = parameters;
@@ -118,21 +120,24 @@
 
   @Test
   public void lookupSingleTarget() {
+    DexProgramClass bClass = appInfo.definitionForProgramType(methodOnBReference.holder);
+    ProgramMethod methodOnB = bClass.lookupProgramMethod(methodOnBReference);
     ResolutionResult resolutionResult =
-        appInfo.resolveMethodOnInterface(methodOnB.holder, methodOnB);
+        appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference);
     DexEncodedMethod resolved = resolutionResult.getSingleTarget();
-    assertEquals(methodOnB, resolved.method);
+    assertEquals(methodOnBReference, resolved.method);
     assertFalse(resolutionResult.isVirtualTarget());
     DexEncodedMethod singleVirtualTarget =
-        appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder, false);
+        appInfo.lookupSingleVirtualTarget(methodOnBReference, methodOnB, false);
     Assert.assertNull(singleVirtualTarget);
   }
 
   @Test
   public void lookupVirtualTargets() {
-    ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(methodOnB.holder, methodOnB);
+    ResolutionResult resolutionResult =
+        appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference);
     DexEncodedMethod resolved = resolutionResult.getSingleTarget();
-    assertEquals(methodOnB, resolved.method);
+    assertEquals(methodOnBReference, resolved.method);
     assertFalse(resolutionResult.isVirtualTarget());
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
index 1f24983..fe66478 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
@@ -165,18 +166,19 @@
 
   @Test
   public void lookupSingleTarget() {
-    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
+    DexProgramClass bClass = appInfo.definitionForProgramType(methodOnB.holder);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
     DexEncodedMethod resolved = resolutionResult.getSingleTarget();
     assertEquals(methodOnA, resolved.method);
     assertFalse(resolutionResult.isVirtualTarget());
     DexEncodedMethod singleVirtualTarget =
-        appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder, false);
+        appInfo.lookupSingleVirtualTarget(methodOnB, bClass.getProgramDefaultInitializer(), false);
     Assert.assertNull(singleVirtualTarget);
   }
 
   @Test
   public void lookupVirtualTargets() {
-    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
     DexEncodedMethod resolved = resolutionResult.getSingleTarget();
     assertEquals(methodOnA, resolved.method);
     assertFalse(resolutionResult.isVirtualTarget());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 67bf3e3..65e2ed5 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -120,7 +120,7 @@
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(declaredClassDefinition, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
 
     if (!symbolicReferenceIsDefiningType) {
       // The targeted method is a private interface method and thus not a maximally specific method.
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index a25f3c6..364014c 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -120,7 +120,7 @@
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(declaredClassDefinition, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
 
     // The targeted method is a private interface method and thus not a maximally specific method.
     assertTrue(resolutionResult instanceof NoSuchMethodResult);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 026055c..25a5d9e 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -95,7 +95,7 @@
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(declaredClassDefinition, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
 
     // Verify that the resolved method is on the defining class.
     assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index f1e182a..4453f58 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -121,7 +121,7 @@
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(declaredClassDefinition, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
 
     // Resolution fails when there is a mismatch between the symbolic reference and the definition.
     if (!symbolicReferenceIsDefiningType) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index 4f013bb..aced457 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -97,7 +97,7 @@
 
     // Resolve the method from the point of the declared holder.
     assertEquals(method.holder, declaredClassDefinition.type);
-    ResolutionResult resolutionResult = appInfo.resolveMethod(declaredClassDefinition, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
 
     // Verify that the resolved method is on the defining class.
     assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 15f7edc..2000456 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -70,7 +70,7 @@
     DexProgramClass bClass =
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 4041e47..0460ce5 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -72,7 +72,7 @@
     DexProgramClass bClass =
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index 8dc4191..5e2743c 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -72,7 +72,7 @@
     DexProgramClass bClass =
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index d6e6ab9..4aff595 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -71,7 +71,7 @@
     DexProgramClass bClass =
         appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
index 020ec02..f2d4c5c 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
@@ -56,7 +56,7 @@
     DexProgramClass aClass =
         appInfo.definitionFor(buildType(A.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
index 4616af8..4d0f445 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
@@ -4,17 +4,17 @@
 package com.android.tools.r8.resolution.access.indirectfield;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.resolution.access.indirectfield.pkg.C;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -53,18 +53,17 @@
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexProgramClass cClass =
         appInfo.definitionFor(buildType(C.class, appInfo.dexItemFactory())).asProgramClass();
+    ProgramMethod barMethod =
+        cClass.lookupProgramMethod(
+            buildMethod(C.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()));
     DexField f =
         buildField(
             // Reflecting on B.class.getField("f") will give A.f, so manually create the reference.
             Reference.field(Reference.classFromClass(B.class), "f", Reference.INT),
             appInfo.dexItemFactory());
-    DexClass initialResolutionHolder = appInfo.definitionFor(f.holder);
-    DexEncodedField resolutionTarget = appInfo.resolveField(f);
-    // TODO(b/145723539): Test access via the resolution result once possible.
-    assertEquals(
-        OptionalBool.TRUE,
-        AccessControl.isFieldAccessible(
-            resolutionTarget, initialResolutionHolder, cClass, appInfo));
+    FieldResolutionResult resolutionResult = appInfo.resolveField(f);
+    assertTrue(resolutionResult.isSuccessfulResolution());
+    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(barMethod, appInfo));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
index bdd8484..6e51158 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
@@ -61,7 +61,7 @@
     DexProgramClass cClass =
         appInfo.definitionFor(buildType(C.class, appInfo.dexItemFactory())).asProgramClass();
     DexMethod bar = buildMethod(B.class.getMethod("foo"), appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
     assertEquals(
         OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo));
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
index b6023ce..b031eca 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -44,7 +44,7 @@
     AndroidApp app = readClasses(CLASSES);
     AppInfoWithLiveness appInfo = computeAppViewWithLiveness(app, Main.class).appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     // Currently R8 will resolve to L::f as that is the first in the topological search.
     // Resolution may return any of the matches, so it is valid if this expectation changes.
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
index 2068ef1..429b8d9 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -49,7 +49,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
index 64908c4..a1e3185 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -49,7 +49,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
index 4841f77..44c41eb 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -52,7 +52,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
index 6059ea1..7683037 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -52,7 +52,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
index 433575f..6fa3216 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -51,7 +51,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     Set<String> holders = new HashSet<>();
     resolutionResult
         .asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
index e04908b..ccac0ed 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -43,7 +43,7 @@
     AppInfoWithLiveness appInfo =
         computeAppViewWithLiveness(readClasses(CLASSES), Main.class).appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
index 7472e89..54a9b59 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -43,7 +43,7 @@
     AppInfoWithLiveness appInfo =
         computeAppViewWithLiveness(readClasses(CLASSES), Main.class).appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
index 5966b9d..a311b5a 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -52,7 +52,7 @@
                 Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     Set<String> holders = new HashSet<>();
     resolutionResult
         .asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
index e59009e..8d2a599 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -59,7 +59,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
@@ -102,7 +102,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
index cb44081..ef5656d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -57,7 +57,7 @@
             buildClasses(I.class, J.class, A.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
index 9a17065..e29f3fc 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
@@ -65,7 +65,7 @@
         AssertionError.class,
         () ->
             appInfo
-                .resolveMethod(method.holder, method)
+                .resolveMethodOnInterface(method)
                 .lookupVirtualDispatchTargets(context, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index 81ed494..b4009fb 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -56,7 +56,7 @@
         AssertionError.class,
         () ->
             appInfo
-                .resolveMethod(method.holder, method)
+                .resolveMethodOnInterface(method)
                 .lookupVirtualDispatchTargets(context, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
index f0b55f2..1033136 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -68,7 +68,7 @@
             buildClasses(C.class, Main.class).addClasspathFiles(classPathJar).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(Abstract.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Abstract.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
index 01d5afb..3a20a43 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
@@ -56,7 +56,7 @@
             buildClasses(A.class, B.class, C.class, D.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
index 37d915a..81920cb 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
@@ -60,7 +60,7 @@
             buildClasses(A.class, B.class, C.class, D.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
index e8c34d1..a964f73 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
@@ -72,7 +72,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
index 338d00a..ed64145 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -71,7 +71,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
index e7d36e9..d86de60 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
@@ -57,7 +57,7 @@
             buildClasses(A.class, B.class, C.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
index c15c379..abd4956 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -52,11 +53,15 @@
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeB = buildType(B.class, appInfo.dexItemFactory());
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+    DexMethod mainMethodReference =
+        buildMethod(Main.class.getDeclaredMethod("main", String[].class), appInfo.dexItemFactory());
+    ProgramMethod mainMethod =
+        appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
     DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
     ClassTypeElement latticeB =
         ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
     DexEncodedMethod singleTarget =
-        appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+        appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeB);
     assertNotNull(singleTarget);
     DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
     assertEquals(fooB, singleTarget.method);
@@ -72,11 +77,15 @@
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeB = buildType(B.class, appInfo.dexItemFactory());
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+    DexMethod mainMethodReference =
+        buildMethod(Main.class.getDeclaredMethod("main", String[].class), appInfo.dexItemFactory());
+    ProgramMethod mainMethod =
+        appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
     DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
     ClassTypeElement latticeB =
         ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
     DexEncodedMethod singleTarget =
-        appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+        appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeB);
     assertNotNull(singleTarget);
     DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
     assertEquals(fooB, singleTarget.method);
@@ -94,10 +103,14 @@
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeC = buildType(C.class, appInfo.dexItemFactory());
     DexType typeMain = buildType(MainAllInstantiated.class, appInfo.dexItemFactory());
+    DexMethod mainMethodReference =
+        buildMethod(Main.class.getDeclaredMethod("main", String[].class), appInfo.dexItemFactory());
+    ProgramMethod mainMethod =
+        appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
     DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
     DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
     DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolution = appInfo.resolveMethod(typeA, fooA);
+    ResolutionResult resolution = appInfo.resolveMethodOnClass(fooA);
     DexProgramClass context = appView.definitionForProgramType(typeMain);
     DexProgramClass upperBound = appView.definitionForProgramType(typeA);
     DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
@@ -120,7 +133,7 @@
     ClassTypeElement latticeC =
         ClassTypeElement.create(typeC, Nullability.definitelyNotNull(), appView);
     DexEncodedMethod singleTarget =
-        appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeC);
+        appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeC);
     assertNull(singleTarget);
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
index 870c9cc..9489060 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
@@ -15,6 +15,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.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.ArrayList;
 import org.junit.Test;
@@ -42,14 +43,18 @@
             factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+    DexMethod mainMethodReference =
+        buildMethod(Main.class.getDeclaredMethod("main", String[].class), appInfo.dexItemFactory());
+    ProgramMethod mainMethod =
+        appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
     DexEncodedMethod singleTarget =
-        appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, null);
+        appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, null);
     assertNotNull(singleTarget);
     assertEquals(fooA, singleTarget.method);
     DexEncodedMethod invalidSingleTarget =
-        appInfo.lookupSingleVirtualTarget(fooA, typeMain, true, t -> false, typeA, null);
+        appInfo.lookupSingleVirtualTarget(fooA, mainMethod, true, t -> false, typeA, null);
     assertNull(invalidSingleTarget);
   }
 
@@ -61,15 +66,19 @@
             factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+    DexMethod mainMethodReference =
+        buildMethod(Main.class.getDeclaredMethod("main", String[].class), appInfo.dexItemFactory());
+    ProgramMethod mainMethod =
+        appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
     DexType typeA = buildType(I.class, appInfo.dexItemFactory());
     DexMethod fooI = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
     DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
     DexEncodedMethod singleTarget =
-        appInfo.lookupSingleVirtualTarget(fooI, typeMain, true, t -> false, typeA, null);
+        appInfo.lookupSingleVirtualTarget(fooI, mainMethod, true, t -> false, typeA, null);
     assertNotNull(singleTarget);
     assertEquals(fooA, singleTarget.method);
     DexEncodedMethod invalidSingleTarget =
-        appInfo.lookupSingleVirtualTarget(fooI, typeMain, false, t -> false, typeA, null);
+        appInfo.lookupSingleVirtualTarget(fooI, mainMethod, false, t -> false, typeA, null);
     assertNull(invalidSingleTarget);
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
index 750c642..7f52044 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -56,7 +56,7 @@
             buildClasses(A.class, B.class, C.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
index 8bc102b..6264f35 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -56,7 +56,7 @@
             buildClasses(I.class, J.class, A.class, B.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
index 469c0ad..8f818b2 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -56,7 +56,7 @@
             buildClasses(I.class, J.class, A.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
index f025e17..2b236b5 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -59,7 +59,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
index d8f8c4a..b75b7ac 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
@@ -58,7 +58,7 @@
                       Main.class);
               AppInfoWithLiveness appInfo = appView.appInfo();
               DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-              ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+              ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
               assertTrue(resolutionResult.isSingleResolution());
               DexType mainType = buildType(Main.class, appInfo.dexItemFactory());
               DexProgramClass main = appView.definitionForProgramType(mainType);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
index 472f7a1..9c7245c 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -55,7 +55,7 @@
         computeAppViewWithLiveness(buildClasses(A.class, I.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index d3c1b7a..745ff4b 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -86,7 +86,7 @@
             });
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(initial, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
@@ -233,7 +233,7 @@
                 .build());
     AppInfoWithClassHierarchy appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexType typeA = buildType(A.class, appInfo.dexItemFactory());
     DexType typeB = buildType(B.class, appInfo.dexItemFactory());
     DexProgramClass classB = appInfo.definitionForProgramType(typeB);
@@ -266,7 +266,7 @@
         computeAppViewWithLiveness(buildClasses(Unrelated.class).build(), Unrelated.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(Unrelated.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
index 477bed8..c74b196 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -61,7 +61,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(Top.class, "clear", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(TopRunner.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
index b9f8c90..e5904db 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -68,7 +68,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(
             buildType(ViewModelRunner.class, appInfo.dexItemFactory()));
@@ -116,7 +116,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
@@ -162,7 +162,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(
             buildType(ViewModelRunnerWithCast.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
index c4fb6d2..e7319cd 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
@@ -60,7 +60,7 @@
             Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(B.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index b6f838b..fbf8ac5 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -57,7 +57,7 @@
             buildClasses(I.class, A.class, B.class, C.class, Main.class).build(), Main.class);
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
-    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
     DexProgramClass context =
         appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
     LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);