Use DexProgramClass and ProgramMethod for contexts

Bug: 155435595
Change-Id: I945e7df5a31be2820f5bddb460c7aff3be93d8f1
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 9a26221..d7ef578 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -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;
       }
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 3a7bad1..d5f8faa 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -208,31 +208,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;
     }
@@ -243,20 +231,15 @@
    * 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;
     }
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 fc83884..7853a16 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -329,26 +329,32 @@
    * <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).getResolvedField();
     return result == null || result.accessFlags.isStatic() ? null : result;
   }
 
+  public DexEncodedField lookupInstanceTarget(DexField field) {
+    return lookupInstanceTargetOn(field.type, 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).getResolvedField();
     return result == null || !result.accessFlags.isStatic() ? null : result;
   }
 
+  public DexEncodedField lookupStaticTarget(DexField field) {
+    return lookupStaticTargetOn(field.type, field);
+  }
+
   /**
    * Lookup static method following the super chain from the holder of {@code method}.
    *
@@ -358,16 +364,13 @@
    * @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) {
+  public DexEncodedMethod lookupStaticTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupStaticTarget(method, toProgramClass(invocationContext));
+    return resolveMethod(method.holder, method).lookupInvokeStaticTarget(context, this);
   }
 
-  public final DexEncodedMethod lookupStaticTarget(
-      DexMethod method, DexProgramClass invocationContext) {
-    assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeStaticTarget(invocationContext, this);
+  public DexEncodedMethod lookupStaticTarget(DexMethod method, ProgramMethod context) {
+    return lookupStaticTarget(method, context.getHolder());
   }
 
   /**
@@ -377,19 +380,16 @@
    * 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) {
+  public DexEncodedMethod lookupSuperTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupSuperTarget(method, toProgramClass(invocationContext));
+    return resolveMethod(method.holder, method).lookupInvokeSuperTarget(context, this);
   }
 
-  public final DexEncodedMethod lookupSuperTarget(
-      DexMethod method, DexProgramClass invocationContext) {
-    assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeSuperTarget(invocationContext, this);
+  public DexEncodedMethod lookupSuperTarget(DexMethod method, ProgramMethod context) {
+    return lookupSuperTarget(method, context.getHolder());
   }
 
   /**
@@ -400,14 +400,12 @@
    * @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) {
+  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass context) {
     assert checkIfObsolete();
-    return lookupDirectTarget(method, toProgramClass(invocationContext));
+    return resolveMethod(method.holder, method).lookupInvokeDirectTarget(context, this);
   }
 
-  public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass invocationContext) {
-    assert checkIfObsolete();
-    return resolveMethod(method.holder, method).lookupInvokeDirectTarget(invocationContext, this);
+  public DexEncodedMethod lookupDirectTarget(DexMethod method, ProgramMethod context) {
+    return lookupDirectTarget(method, context.getHolder());
   }
 }
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 7a21ff1..0b9b084 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -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/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/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
index 266a23b..890d030 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -17,7 +17,7 @@
   }
 
   public abstract OptionalBool isAccessibleFrom(
-      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+      ProgramMethod context, AppInfoWithClassHierarchy appInfo);
 
   public boolean isSuccessfulResolution() {
     return false;
@@ -59,10 +59,9 @@
     }
 
     @Override
-    public OptionalBool isAccessibleFrom(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+    public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
       return AccessControl.isFieldAccessible(
-          resolvedField, initialResolutionHolder, context, appInfo);
+          resolvedField, initialResolutionHolder, context.getHolder(), appInfo);
     }
 
     @Override
@@ -81,8 +80,7 @@
     private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult();
 
     @Override
-    public OptionalBool isAccessibleFrom(
-        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+    public OptionalBool isAccessibleFrom(ProgramMethod context, AppInfoWithClassHierarchy appInfo) {
       return OptionalBool.FALSE;
     }
 
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/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/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 3335e27..6264490 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
@@ -13,6 +13,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.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -97,7 +98,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`.
@@ -307,7 +308,7 @@
     public static boolean forInvokeInterface(
         InvokeInterface instruction,
         DexType type,
-        DexType context,
+        ProgramMethod context,
         AppView<?> appView,
         Query mode,
         AnalysisAssumption assumption) {
@@ -343,7 +344,7 @@
     public static boolean forInvokeStatic(
         InvokeStatic instruction,
         DexType type,
-        DexType context,
+        ProgramMethod context,
         AppView<?> appView,
         Query mode,
         AnalysisAssumption assumption) {
@@ -358,7 +359,7 @@
     public static boolean forInvokeSuper(
         InvokeSuper instruction,
         DexType type,
-        DexType context,
+        ProgramMethod context,
         AppView<?> appView,
         Query mode,
         AnalysisAssumption assumption) {
@@ -402,7 +403,7 @@
     public static boolean forInvokeVirtual(
         InvokeVirtual instruction,
         DexType type,
-        DexType context,
+        ProgramMethod context,
         AppView<?> appView,
         Query mode,
         AnalysisAssumption assumption) {
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 d6b0ba1..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;
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 67d9f3c..4b209ac 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
@@ -80,7 +80,7 @@
           if (field != null) {
             if (fieldAssignmentTracker != null) {
               fieldAssignmentTracker.recordFieldAccess(
-                  fieldInstruction, field.getDefinition(), code.method());
+                  fieldInstruction, field.getDefinition(), code.context());
             }
             if (fieldBitAccessAnalysis != null) {
               fieldBitAccessAnalysis.recordFieldAccess(
@@ -93,7 +93,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 0f6e2ac..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;
   }
 
@@ -324,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/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 5042ab4..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() {
@@ -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 980b1a3..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
@@ -209,9 +209,9 @@
       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 d6d83ac..0ee0bc8 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
@@ -434,10 +434,10 @@
           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.getReference(), dynamicMethod);
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 4752d83..da7fab0 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;
@@ -39,7 +39,7 @@
    */
   public static ClassInitializerSideEffect classInitializerCanBePostponed(
       AppView<?> appView, IRCode code) {
-    DexType context = code.method().holder();
+    ProgramMethod context = code.context();
     OptionalBool controlFlowMayDependOnEnvironment = OptionalBool.unknown();
     boolean mayHaveSideEffects = false;
 
@@ -114,7 +114,7 @@
         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..62a3aa6 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;
   }
 
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..274a03f 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;
@@ -99,7 +100,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 +109,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 +134,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 +165,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 +190,7 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> 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..8845669 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;
@@ -333,7 +334,7 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> 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..9a2483c 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;
@@ -156,12 +157,12 @@
   }
 
   @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<?> 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 e131f9e..6b8b667 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
@@ -11,7 +11,7 @@
 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.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,19 +61,19 @@
   }
 
   @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) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     DexEncodedField resolvedField;
     if (appView.enableWholeProgramOptimizations()) {
       // TODO(b/123857022): Should be possible to use definitionFor().
       resolvedField = appView.appInfo().resolveField(field).getResolvedField();
     } else {
       // In D8, only allow the field in the same context.
-      if (field.holder != context) {
+      if (field.holder != context.getHolderType()) {
         return AbstractError.top();
       }
       // Note that, in D8, we are not using AppInfo#resolveField to avoid traversing the hierarchy.
@@ -138,7 +138,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 +153,7 @@
   }
 
   @Override
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<?> appView, ProgramMethod context) {
     if (instructionMayTriggerMethodInvocation(appView, context)) {
       // This may trigger class initialization, which could potentially read any field.
       return UnknownFieldSet.getInstance();
@@ -228,7 +228,7 @@
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> appView, ProgramMethod context) {
     assert isFieldGet();
     DexEncodedField field = appView.appInfo().resolveField(getField()).getResolvedField();
     if (field != null) {
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..14d9a56 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;
@@ -78,7 +79,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -92,14 +93,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 +109,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 +147,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..12dfa1d 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;
@@ -116,7 +117,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 +128,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 +152,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 +205,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return object() == value;
   }
 
@@ -221,7 +222,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -230,7 +231,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 b998022..84ea9dc 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();
@@ -143,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;
@@ -191,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
@@ -233,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;
   }
 
@@ -250,7 +251,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -259,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..bd8d7fe 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;
@@ -139,7 +140,7 @@
     return oldOutValue;
   }
 
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> appView, ProgramMethod context) {
     assert hasOutValue();
     return UnknownValue.getInstance();
   }
@@ -532,7 +533,8 @@
     return false;
   }
 
-  public boolean isBlockLocalInstructionWithoutSideEffects(AppView<?> appView, DexType context) {
+  public boolean isBlockLocalInstructionWithoutSideEffects(
+      AppView<?> appView, ProgramMethod context) {
     return definesBlockLocalValue() && !instructionMayHaveSideEffects(appView, context);
   }
 
@@ -570,12 +572,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 +586,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 +603,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<?> appView, ProgramMethod context) {
     if (instructionMayTriggerMethodInvocation(appView, context)
         && instructionMayHaveSideEffects(appView, context)) {
       return UnknownFieldSet.getInstance();
@@ -1378,7 +1380,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 +1434,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,7 +1462,7 @@
    */
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
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..edf535f 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,24 @@
   }
 
   @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.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,7 +150,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -159,7 +160,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 +218,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 +237,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 +268,7 @@
   }
 
   @Override
-  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+  public AbstractFieldSet readSet(AppView<?> 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..017e172 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,14 +87,14 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
+  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
     if (appView.appInfo().hasLiveness()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       return appViewWithLiveness
           .appInfo()
           .lookupSingleVirtualTarget(
               getInvokedMethod(),
-              invocationContext,
+              context,
               true,
               appView,
               TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
@@ -104,8 +105,8 @@
 
   @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,7 +117,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
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..36398d5 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(
@@ -118,22 +114,25 @@
     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 +194,17 @@
   }
 
   @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<?> appView, ProgramMethod context) {
     return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
   }
 
   @Override
-  public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
+  public AbstractValue getAbstractValue(AppView<?> appView, ProgramMethod context) {
     assert hasOutValue();
     DexEncodedMethod method = lookupSingleTarget(appView, context);
     if (method != null) {
@@ -224,7 +223,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..83799e7 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
@@ -53,7 +53,7 @@
   }
 
   @Override
-  public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+  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..2b64167 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,7 +148,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -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..0df812c 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,12 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
-    if (appView.appInfo().hasLiveness() && invocationContext != null) {
+  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+    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,14 +107,14 @@
 
   @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,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index b9a0765..464fdc2 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,19 @@
   }
 
   @Override
-  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
-    return lookupSingleTarget(appView, invocationContext, getInvokedMethod(), getReceiver());
+  public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+    return lookupSingleTarget(appView, context, getInvokedMethod(), getReceiver());
   }
 
   public static DexEncodedMethod lookupSingleTarget(
-      AppView<?> appView, DexType invocationContext, DexMethod method, Value receiver) {
+      AppView<?> appView, ProgramMethod context, DexMethod method, Value receiver) {
     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 +127,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,7 +139,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -148,7 +149,7 @@
 
   @Override
   public boolean instructionMayHaveSideEffects(
-      AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+      AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     if (!appView.enableWholeProgramOptimizations()) {
       return true;
     }
@@ -205,6 +206,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..5141ff7 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;
@@ -100,8 +101,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,7 +133,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -142,7 +143,7 @@
 
   @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)
@@ -175,7 +176,7 @@
     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 -> appView.isSubtype(context.getHolderType(), type).isTrue(),
         Sets.newIdentityHashSet())) {
       return true;
     }
@@ -196,7 +197,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 +209,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..ec7c00d 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;
@@ -136,13 +137,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 +154,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 +178,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,7 +227,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -240,7 +241,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 +249,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 29cd30d..9a8f158 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();
@@ -140,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;
@@ -188,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
@@ -227,7 +228,7 @@
   @Override
   public boolean definitelyTriggersClassInitialization(
       DexType clazz,
-      DexType context,
+      ProgramMethod context,
       AppView<?> appView,
       Query mode,
       AnalysisAssumption assumption) {
@@ -236,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
@@ -244,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..48ffd21 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();
     }
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 4855f96..b678f63 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,10 +165,9 @@
     }
 
     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) {
@@ -176,14 +175,14 @@
         ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
         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 +193,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) {
@@ -219,7 +218,7 @@
                 if (resolution.isVirtualTarget()) {
                   LookupResult lookupResult =
                       resolution.lookupVirtualDispatchTargets(
-                          appView.definitionForProgramType(context), appView.appInfo());
+                          context.getHolder(), appView.appInfo());
                   if (lookupResult.isLookupResultSuccess()) {
                     ProgramMethodSet targets = ProgramMethodSet.create();
                     lookupResult
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 acb21d7..24b3df4 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
@@ -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) {
@@ -1637,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(
@@ -1716,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/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index a640fb6..a45bc26 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
@@ -136,7 +136,7 @@
             appView
                 .appInfo()
                 .withClassHierarchy()
-                .lookupSuperTarget(invoke.getInvokedMethod(), code.method().holder());
+                .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/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 5d494bb..a7827eb 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
@@ -317,7 +317,7 @@
               DexEncodedMethod dexEncodedMethod =
                   appView
                       .appInfo()
-                      .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method().holder());
+                      .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.context());
               if (dexEncodedMethod != null) {
                 DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder());
                 if (dexClass != null && dexClass.isLibraryClass()) {
@@ -1123,7 +1123,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..ab75872 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
@@ -75,7 +75,7 @@
 
   LambdaClass(
       LambdaRewriter rewriter,
-      DexType accessedFrom,
+      ProgramMethod accessedFrom,
       DexType lambdaClassType,
       LambdaDescriptor descriptor) {
     assert rewriter != null;
@@ -108,16 +108,16 @@
 
   // Generate unique lambda class type for lambda descriptor and instantiation point context.
   static DexType createLambdaClassType(
-      LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
+      LambdaRewriter rewriter, ProgramMethod accessedFrom, LambdaDescriptor match) {
     return createLambdaClassType(rewriter.getFactory(), accessedFrom, match);
   }
 
   public static DexType createLambdaClassType(
-      DexItemFactory factory, DexType accessedFrom, LambdaDescriptor match) {
+      DexItemFactory factory, 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,7 +129,7 @@
     // 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
@@ -337,7 +337,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,14 +360,14 @@
     }
   }
 
-  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();
@@ -412,7 +412,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();
 
@@ -437,14 +437,15 @@
     DexMethod accessorMethod =
         rewriter
             .getFactory()
-            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+            .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)) {
@@ -458,7 +459,7 @@
         rewriter
             .getFactory()
             .createMethod(
-                accessedFrom,
+                accessedFrom.getHolderType(),
                 descriptor.implHandle.asMethod().proto,
                 generateUniqueLambdaMethodName());
     return new ClassMethodWithAccessorTarget(accessorMethod);
@@ -466,7 +467,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();
@@ -486,12 +487,13 @@
     DexMethod accessorMethod =
         rewriter
             .getFactory()
-            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+            .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);
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..49c19f4 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,8 +112,8 @@
   }
 
   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) {
@@ -123,7 +122,7 @@
           DexEncodedMethod target =
               appInfo.resolveMethod(getImplReceiverType(), method).getSingleTarget();
         if (target == null) {
-            target = appInfo.lookupDirectTarget(method, invocationContext);
+            target = appInfo.lookupDirectTarget(method, context);
         }
         assert target == null
             || (implHandle.type.isInvokeInstance() && isInstanceMethod(target))
@@ -133,13 +132,13 @@
       }
 
       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;
       }
@@ -187,7 +186,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 +216,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 +239,10 @@
     }
 
     boolean accessedFromSamePackage =
-        accessedFrom.getPackageDescriptor().equals(targetHolder.getPackageDescriptor());
+        accessedFrom
+            .getHolderType()
+            .getPackageDescriptor()
+            .equals(targetHolder.getPackageDescriptor());
     assert flags.isProtected() || accessedFromSamePackage;
     return flags.isProtected() && !accessedFromSamePackage;
   }
@@ -248,8 +252,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 +269,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 +313,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..2bbfb30 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
@@ -138,7 +138,7 @@
    */
   public void desugarLambdas(DexEncodedMethod encodedMethod, 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 +147,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;
           }
@@ -157,8 +156,8 @@
           // during IR processing, and therefore we may need to create it now.
           LambdaClass lambdaClass =
               getAppView().enableWholeProgramOptimizations()
-                  ? getKnownLambdaClass(descriptor, currentType)
-                  : getOrCreateLambdaClass(descriptor, currentType);
+                  ? getKnownLambdaClass(descriptor, context)
+                  : getOrCreateLambdaClass(descriptor, context);
           assert lambdaClass != null;
 
           // We rely on patch performing its work in a way which
@@ -214,7 +213,7 @@
   // 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,
@@ -223,9 +222,7 @@
     return descriptor != null
         ? descriptor
         : putIfAbsent(
-            knownCallSites,
-            callSite,
-            LambdaDescriptor.infer(callSite, getAppInfo(), invocationContext));
+            knownCallSites, callSite, LambdaDescriptor.infer(callSite, getAppInfo(), context));
   }
 
   private boolean isInMainDexList(DexType type) {
@@ -234,7 +231,8 @@
 
   // 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) {
+  public LambdaClass getOrCreateLambdaClass(
+      LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
     DexType lambdaClassType = LambdaClass.createLambdaClassType(this, accessedFrom, descriptor);
     // We check the map twice to to minimize time spent in synchronized block.
     LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
@@ -246,35 +244,36 @@
               new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
       if (getAppView().options().isDesugaredLibraryCompilation()) {
         DexType rewrittenType =
-            getAppView().rewritePrefix.rewrittenType(accessedFrom, getAppView());
+            getAppView().rewritePrefix.rewrittenType(accessedFrom.getHolderType(), getAppView());
         if (rewrittenType == null) {
           rewrittenType =
               getAppView()
                   .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) {
+  private LambdaClass getKnownLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
     DexType lambdaClassType = LambdaClass.createLambdaClassType(this, 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('.'));
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..a573863 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;
@@ -97,24 +96,26 @@
           ResolutionResult resolutionResult =
               appView.appInfo().resolveMethod(invokedMethod.holder, invokedMethod);
           // 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());
         }
       }
@@ -147,8 +148,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 +157,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 +201,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 baaed33e..e10d477 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;
@@ -30,6 +29,7 @@
 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;
@@ -124,18 +124,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();
     }
 
@@ -144,7 +143,8 @@
     // the field initial value.
     Set<StaticPut> unnecessaryStaticPuts = Sets.newIdentityHashSet();
     Collection<StaticPut> finalFieldPuts =
-        findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(code, clazz, unnecessaryStaticPuts);
+        findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(
+            code, context, unnecessaryStaticPuts);
 
     // Return eagerly if there is nothing to optimize.
     if (finalFieldPuts.isEmpty()) {
@@ -161,7 +161,7 @@
       Value value = put.value();
       if (unnecessaryStaticPuts.contains(put)) {
         if (fieldType == dexItemFactory.stringType) {
-          fieldsWithStaticValues.put(field, getDexStringValue(value, method.holder()));
+          fieldsWithStaticValues.put(field, getDexStringValue(value, context.getHolderType()));
         } else if (fieldType.isClassType() || fieldType.isArrayType()) {
           if (value.isZero()) {
             fieldsWithStaticValues.put(field, DexValueNull.NULL);
@@ -361,7 +361,7 @@
   }
 
   private Collection<StaticPut> findFinalFieldPutsWhileCollectingUnnecessaryStaticPuts(
-      IRCode code, DexClass clazz, Set<StaticPut> unnecessaryStaticPuts) {
+      IRCode code, ProgramMethod context, Set<StaticPut> unnecessaryStaticPuts) {
     ValueMayDependOnEnvironmentAnalysis environmentAnalysis =
         new ValueMayDependOnEnvironmentAnalysis(appView, code);
     Map<DexField, StaticPut> finalFieldPuts = Maps.newIdentityHashMap();
@@ -377,22 +377,22 @@
             // 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()).getResolvedField();
-            if (field != null && field.holder() == clazz.type) {
+            if (field != null && field.holder() == context.getHolderType()) {
               isReadBefore.add(field.field);
-            } else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
+            } else if (instruction.instructionMayHaveSideEffects(appView, context)) {
               // 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);
@@ -400,7 +400,7 @@
             DexField field = put.getField();
             Value value = put.value();
             TypeElement valueType = value.getType();
-            if (clazz.definesStaticField(field)) {
+            if (context.getHolder().definesStaticField(field)) {
               if (isReadBefore.contains(field)) {
                 // Promoting this put to a class constant would cause a previous static-get
                 // instruction to read a different value.
@@ -434,7 +434,7 @@
                 // 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);
@@ -458,13 +458,13 @@
               // 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;
             }
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..8b46005 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, invokedMethod, invoke.getReceiver());
               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;
     }
@@ -318,10 +319,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 +332,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 f2e3092..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;
           }
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..6fb87b6 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();
     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,9 +387,7 @@
   }
 
   private ConstraintWithTarget forVirtualInvoke(
-      DexMethod method,
-      DexType invocationContext,
-      boolean isInterface) {
+      DexMethod method, DexProgramClass context, boolean isInterface) {
     if (method.holder.isArrayType()) {
       return ConstraintWithTarget.ALWAYS;
     }
@@ -428,7 +411,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 +419,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 900d62a..a2072ee 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
@@ -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.
@@ -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();
@@ -494,7 +494,7 @@
       return;
     }
 
-    replaceInstructionByNullCheckIfPossible(current, iterator, code.method().holder());
+    replaceInstructionByNullCheckIfPossible(current, iterator, code.context());
   }
 
   private void replaceStaticPutByInitClassIfNeverRead(
@@ -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 9bee381..d0047d3 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
@@ -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();
 
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..92ba7c6 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
@@ -863,8 +863,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;
       }
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 512e33d..7330d11 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
@@ -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 6a59224..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,7 +451,7 @@
       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)) {
@@ -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 24fc2f7..ceee8d2 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,7 +172,7 @@
     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()).getResolvedField();
@@ -273,8 +273,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.
           }
@@ -580,7 +579,7 @@
           continue;
         }
 
-        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType());
+        DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
         if (singleTarget != null) {
           Predicate<InvokeMethod> noSideEffectsPredicate =
               dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault(
@@ -1175,7 +1174,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 608ba09..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;
       }
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..c1064a6 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
@@ -9,6 +9,7 @@
 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;
@@ -154,11 +155,11 @@
 
   public static CallSiteOptimizationInfo fromArguments(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      DexEncodedMethod method,
+      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 +168,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 +181,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 2464952..b06b4a8 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,7 +221,7 @@
               }
             }
             DexField field = insn.asFieldInstruction().getField();
-            if (appView.appInfo().resolveFieldOn(clazz, field).isSuccessfulResolution()) {
+            if (appView.appInfo().resolveField(field).isSuccessfulResolution()) {
               // Require only accessing direct or indirect instance fields of the current class.
               break;
             }
@@ -236,7 +233,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 +247,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 +268,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 +286,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 +342,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 +377,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 +420,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 +446,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 +500,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;
@@ -525,7 +515,7 @@
                 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.
@@ -546,7 +536,7 @@
               Value object =
                   instancePut.object().getAliasedValue(aliasesThroughAssumeAndCheckCasts);
               if (object != receiver
-                  || instancePut.instructionInstanceCanThrow(appView, clazz.type).isThrowing()) {
+                  || instancePut.instructionInstanceCanThrow(appView, context).isThrowing()) {
                 builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
               }
 
@@ -614,7 +604,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()) {
@@ -646,7 +636,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()
@@ -696,7 +686,7 @@
     if (method.isStatic()) {
       // Identifies if the method preserves class initialization after inlining.
       feedback.markTriggerClassInitBeforeAnySideEffect(
-          method, triggersClassInitializationBeforeSideEffect(method.holder(), code, appView));
+          method, triggersClassInitializationBeforeSideEffect(code, appView));
     } else {
       // Identifies if the method preserves null check of the receiver after inlining.
       final Value receiver = code.getThis();
@@ -712,13 +702,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) {
+      IRCode code, AppView<?> appView) {
     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.
@@ -728,7 +722,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;
           }
@@ -849,7 +843,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
@@ -858,7 +852,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()) {
@@ -1023,7 +1017,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.
@@ -1035,9 +1029,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;
@@ -1046,7 +1040,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`.
@@ -1066,31 +1060,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(clazz, appView.dexItemFactory().objectMembers.finalize);
+    DexEncodedMethod target = resolutionResult.getSingleTarget();
+    return target != null
+        && target.method != dexItemFactory.enumMethods.finalize
+        && target.method != dexItemFactory.objectMembers.finalize;
   }
 
   private void computeReturnValueOnlyDependsOnArguments(
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/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..907db14 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();
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/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index dc9d272..029cd51 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;
 
@@ -126,7 +127,7 @@
   }
 
   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,7 +300,7 @@
   }
 
   private void computeFieldRebindingForIndirectAccessWithContexts(
-      DexField field, Set<DexEncodedMethod> contexts) {
+      DexField field, ProgramMethodSet contexts) {
     DexEncodedField target = appView.appInfo().resolveField(field).getResolvedField();
     if (target == null) {
       assert false;
@@ -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/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 77edcc6..919be25 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,
@@ -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,
@@ -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).
@@ -1140,7 +1137,7 @@
     SingleResolutionResult resolution =
         resolveMethod(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();
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 f8dcf90..3288a39 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -191,11 +191,11 @@
   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;
@@ -705,44 +705,39 @@
   //
 
   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).getResolvedField();
@@ -784,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;
     }
@@ -795,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()) {
@@ -2301,7 +2294,7 @@
   }
 
   public boolean isFieldWrittenInMethodSatisfying(
-      ProgramField field, Predicate<DexEncodedMethod> predicate) {
+      ProgramField field, Predicate<ProgramMethod> predicate) {
     FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.getReference());
     return info != null && info.isWrittenInMethodSatisfying(predicate);
   }
@@ -2426,9 +2419,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;
     }
@@ -2436,7 +2429,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;
     }
 
@@ -2445,7 +2438,7 @@
 
     resolution
         .lookupVirtualDispatchTargets(
-            context,
+            contextHolder,
             appInfo,
             (type, subTypeConsumer, lambdaConsumer) ->
                 objectAllocationInfoCollection.forEachInstantiatedSubType(
@@ -3953,9 +3946,12 @@
         return false;
       }
       if (field.getDefinition().isStatic()) {
-        if (!registerFieldReadFromAnnotation(fieldReference)) {
-          return false;
-        }
+        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.
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/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 31d8074..603cb9d 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(
@@ -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/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e4b20c6..e92effc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -839,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));
     }
   }
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/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..7ec4a08 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,14 @@
     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
+            .resolveMethod(method.getHolderType(), 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 +154,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);
+    ProgramMethod methodXOnTest =
+        methodYOnTest.getHolder().lookupProgramMethod(methodXOnTestReference);
 
     assertFalse(
         appInfo
-            .resolveMethod(classTestSuper, methodXOnTestSuper)
+            .resolveMethod(classTestSuper, methodXOnTestSuper.getReference())
             .getSingleTarget()
             .isVirtualMethod());
-    assertNull(appInfo.resolveMethod(classTest, methodXOnTestSuper).getSingleTarget());
-    assertNull(appInfo.resolveMethod(classTest, methodXOnTest).getSingleTarget());
+    assertNull(
+        appInfo.resolveMethod(classTest, methodXOnTestSuper.getReference()).getSingleTarget());
+    assertNull(appInfo.resolveMethod(classTest, methodXOnTestReference).getSingleTarget());
 
-    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper, methodXOnTestSuper.holder));
-    assertNull(appInfo.lookupDirectTarget(methodXOnTest, methodXOnTest.holder));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestReference, methodXOnTest));
 
-    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, methodXOnTest));
 
     assertEquals("OK", runArt(application));
   }
@@ -211,8 +216,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 +248,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/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/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 7bd9cc9..ac07b70 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,25 @@
         });
   }
 
-  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());
+    DexMethod reference =
+        buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
+    ProgramMethod context =
+        appInfo.definitionForProgramType(reference.holder).getProgramDefaultInitializer();
     Assert.assertNotNull(
-        appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
+        appInfo.resolveMethod(toType(invokeReceiver, appInfo), reference).getSingleTarget());
     DexEncodedMethod singleVirtualTarget =
-        appInfo.lookupSingleVirtualTarget(method, method.holder, false);
+        appInfo.lookupSingleVirtualTarget(reference, context, false);
     if (singleTargetHolderOrNull == null) {
       Assert.assertNull(singleVirtualTarget);
     } else {
@@ -191,7 +194,7 @@
   }
 
   @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());
@@ -203,7 +206,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/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..7c91900 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,12 +166,13 @@
 
   @Test
   public void lookupSingleTarget() {
+    DexProgramClass bClass = appInfo.definitionForProgramType(methodOnB.holder);
     ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
     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);
   }
 
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 6faf65f..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
@@ -14,6 +14,7 @@
 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;
@@ -52,6 +53,9 @@
     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.
@@ -59,7 +63,7 @@
             appInfo.dexItemFactory());
     FieldResolutionResult resolutionResult = appInfo.resolveField(f);
     assertTrue(resolutionResult.isSuccessfulResolution());
-    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(cClass, appInfo));
+    assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(barMethod, appInfo));
   }
 
   @Test
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..48cbcbd 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,6 +103,10 @@
     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());
@@ -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);
   }