Add context to parts of the enqueuer and use it to check access.

Bug: 145187573
Change-Id: I128c1947bdef4802b31014ba71c05abeee96438c
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index d1c82d4..71750f1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -42,7 +42,7 @@
    */
   @Override
   public boolean registerConstClass(DexType type) {
-    if (references.isDynamicMethod(currentMethod)) {
+    if (references.isDynamicMethod(getContextMethod())) {
       return false;
     }
     return super.registerConstClass(type);
@@ -57,7 +57,7 @@
    */
   @Override
   public boolean registerStaticFieldRead(DexField field) {
-    if (references.isDynamicMethod(currentMethod)) {
+    if (references.isDynamicMethod(getContextMethod())) {
       return false;
     }
     return super.registerStaticFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 7c78ac5..f05d068 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -12,100 +12,107 @@
 import com.android.tools.r8.graph.DexMethodHandle;
 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.UseRegistry;
 
 public class DefaultEnqueuerUseRegistry extends UseRegistry {
 
-  private final DexProgramClass currentHolder;
-  protected final DexEncodedMethod currentMethod;
+  private final ProgramMethod context;
   private final Enqueuer enqueuer;
 
   public DefaultEnqueuerUseRegistry(
-      AppView<?> appView,
-      DexProgramClass currentHolder,
-      DexEncodedMethod currentMethod,
-      Enqueuer enqueuer) {
+      AppView<?> appView, DexProgramClass holder, DexEncodedMethod method, Enqueuer enqueuer) {
     super(appView.dexItemFactory());
-    assert currentHolder.type == currentMethod.method.holder;
-    this.currentHolder = currentHolder;
-    this.currentMethod = currentMethod;
+    this.context = new ProgramMethod(holder, method);
     this.enqueuer = enqueuer;
   }
 
+  public ProgramMethod getContext() {
+    return context;
+  }
+
+  public DexProgramClass getContextHolder() {
+    return context.holder;
+  }
+
+  public DexEncodedMethod getContextMethod() {
+    return context.method;
+  }
+
   @Override
   public boolean registerInvokeVirtual(DexMethod invokedMethod) {
-    return enqueuer.traceInvokeVirtual(invokedMethod, currentHolder, currentMethod);
+    return enqueuer.traceInvokeVirtual(invokedMethod, context);
   }
 
   @Override
   public boolean registerInvokeDirect(DexMethod invokedMethod) {
-    return enqueuer.traceInvokeDirect(invokedMethod, currentHolder, currentMethod);
+    return enqueuer.traceInvokeDirect(invokedMethod, context);
   }
 
   @Override
   public boolean registerInvokeStatic(DexMethod invokedMethod) {
-    return enqueuer.traceInvokeStatic(invokedMethod, currentHolder, currentMethod);
+    return enqueuer.traceInvokeStatic(invokedMethod, context);
   }
 
   @Override
   public boolean registerInvokeInterface(DexMethod invokedMethod) {
-    return enqueuer.traceInvokeInterface(invokedMethod, currentHolder, currentMethod);
+    return enqueuer.traceInvokeInterface(invokedMethod, context);
   }
 
   @Override
   public boolean registerInvokeSuper(DexMethod invokedMethod) {
-    return enqueuer.traceInvokeSuper(invokedMethod, currentHolder, currentMethod);
+    return enqueuer.traceInvokeSuper(invokedMethod, context);
   }
 
   @Override
   public boolean registerInstanceFieldWrite(DexField field) {
-    return enqueuer.traceInstanceFieldWrite(field, currentMethod);
+    return enqueuer.traceInstanceFieldWrite(field, context.method);
   }
 
   @Override
   public boolean registerInstanceFieldRead(DexField field) {
-    return enqueuer.traceInstanceFieldRead(field, currentMethod);
+    return enqueuer.traceInstanceFieldRead(field, context.method);
   }
 
   @Override
   public boolean registerNewInstance(DexType type) {
-    return enqueuer.traceNewInstance(type, currentMethod);
+    return enqueuer.traceNewInstance(type, context);
   }
 
   @Override
   public boolean registerStaticFieldRead(DexField field) {
-    return enqueuer.traceStaticFieldRead(field, currentMethod);
+    return enqueuer.traceStaticFieldRead(field, context.method);
   }
 
   @Override
   public boolean registerStaticFieldWrite(DexField field) {
-    return enqueuer.traceStaticFieldWrite(field, currentMethod);
+    return enqueuer.traceStaticFieldWrite(field, context.method);
   }
 
   @Override
   public boolean registerConstClass(DexType type) {
-    return enqueuer.traceConstClass(type, currentMethod);
+    return enqueuer.traceConstClass(type, context.method);
   }
 
   @Override
   public boolean registerCheckCast(DexType type) {
-    return enqueuer.traceCheckCast(type, currentMethod);
+    return enqueuer.traceCheckCast(type, context.method);
   }
 
   @Override
   public boolean registerTypeReference(DexType type) {
-    return enqueuer.traceTypeReference(type, currentMethod);
+    return enqueuer.traceTypeReference(type, context.method);
   }
 
   @Override
   public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
     super.registerMethodHandle(methodHandle, use);
-    enqueuer.traceMethodHandle(methodHandle, use, currentMethod);
+    enqueuer.traceMethodHandle(methodHandle, use, context.method);
   }
 
   @Override
   public void registerCallSite(DexCallSite callSite) {
     super.registerCallSite(callSite);
-    enqueuer.traceCallSite(callSite, currentMethod);
+    enqueuer.traceCallSite(callSite, context);
   }
 }
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 ce71c31..2e2216e 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Descriptor;
@@ -37,6 +38,7 @@
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
 import com.android.tools.r8.graph.KeyedDexItem;
 import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -93,7 +95,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -586,21 +587,20 @@
     return isRead ? info.recordRead(field, context) : info.recordWrite(field, context);
   }
 
-  void traceCallSite(DexCallSite callSite, DexEncodedMethod currentMethod) {
+  void traceCallSite(DexCallSite callSite, ProgramMethod context) {
     callSites.add(callSite);
 
     List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
     if (directInterfaces != null) {
       for (DexType lambdaInstantiatedInterface : directInterfaces) {
-        markLambdaInstantiated(lambdaInstantiatedInterface, currentMethod);
+        markLambdaInstantiated(lambdaInstantiatedInterface, context.method);
       }
     } else {
       if (!appInfo.isStringConcat(callSite.bootstrapMethod)) {
         if (options.reporter != null) {
           Diagnostic message =
               new StringDiagnostic(
-                  "Unknown bootstrap method " + callSite.bootstrapMethod,
-                  appInfo.originFor(currentMethod.method.holder));
+                  "Unknown bootstrap method " + callSite.bootstrapMethod, context.holder.origin);
           options.reporter.warning(message);
         }
       }
@@ -634,19 +634,19 @@
 
     switch (implHandle.type) {
       case INVOKE_STATIC:
-        traceInvokeStaticFromLambda(method, currentMethod);
+        traceInvokeStaticFromLambda(method, context);
         break;
       case INVOKE_INTERFACE:
-        traceInvokeInterfaceFromLambda(method, currentMethod);
+        traceInvokeInterfaceFromLambda(method, context);
         break;
       case INVOKE_INSTANCE:
-        traceInvokeVirtualFromLambda(method, currentMethod);
+        traceInvokeVirtualFromLambda(method, context);
         break;
       case INVOKE_DIRECT:
-        traceInvokeDirectFromLambda(method, currentMethod);
+        traceInvokeDirectFromLambda(method, context);
         break;
       case INVOKE_CONSTRUCTOR:
-        traceNewInstanceFromLambda(method.holder, currentMethod);
+        traceNewInstanceFromLambda(method.holder, context);
         break;
       default:
         throw new Unreachable();
@@ -739,8 +739,9 @@
     return true;
   }
 
-  boolean traceInvokeDirect(
-      DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+  boolean traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
+    DexProgramClass currentHolder = context.holder;
+    DexEncodedMethod currentMethod = context.method;
     boolean skipTracing =
         registerDeferredActionForDeadProtoBuilder(
             invokedMethod.holder,
@@ -753,7 +754,7 @@
     }
 
     return traceInvokeDirect(
-        invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+        invokedMethod, context, KeepReason.invokedFrom(currentHolder, currentMethod));
   }
 
   /** Returns true if a deferred action was registered. */
@@ -770,13 +771,14 @@
     return false;
   }
 
-  boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+  boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeDirect(
-        invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+        invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
   }
 
   private boolean traceInvokeDirect(
-      DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+      DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+    DexEncodedMethod currentMethod = context.method;
     if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, currentMethod)) {
       return false;
     }
@@ -787,42 +789,42 @@
     return true;
   }
 
-  boolean traceInvokeInterface(
-      DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+  boolean traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeInterface(
-        invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+        invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
   }
 
-  boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+  boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeInterface(
-        invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+        invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
   }
 
   private boolean traceInvokeInterface(
-      DexMethod method, DexEncodedMethod currentMethod, KeepReason keepReason) {
+      DexMethod method, ProgramMethod context, KeepReason keepReason) {
+    DexEncodedMethod currentMethod = context.method;
     if (!registerMethodWithTargetAndContext(interfaceInvokes, method, currentMethod)) {
       return false;
     }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Register invokeInterface `%s`.", method);
     }
-    workList.enqueueMarkReachableInterfaceAction(method, keepReason);
+    markVirtualMethodAsReachable(method, true, context, keepReason);
     return true;
   }
 
-  boolean traceInvokeStatic(
-      DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+  boolean traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeStatic(
-        invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+        invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
   }
 
-  boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+  boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeStatic(
-        invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+        invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
   }
 
   private boolean traceInvokeStatic(
-      DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+      DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+    DexEncodedMethod currentMethod = context.method;
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
         || dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -852,8 +854,9 @@
     return true;
   }
 
-  boolean traceInvokeSuper(
-      DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+  boolean traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
+    DexProgramClass currentHolder = context.holder;
+    DexEncodedMethod currentMethod = context.method;
     // We have to revisit super invokes based on the context they are found in. The same
     // method descriptor will hit different targets, depending on the context it is used in.
     DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, currentMethod);
@@ -867,56 +870,55 @@
     return true;
   }
 
-  boolean traceInvokeVirtual(
-      DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+  boolean traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeVirtual(
-        invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+        invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
   }
 
-  boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+  boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, ProgramMethod context) {
     return traceInvokeVirtual(
-        invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+        invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
   }
 
   private boolean traceInvokeVirtual(
-      DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+      DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
     if (invokedMethod == appView.dexItemFactory().classMethods.newInstance
         || invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) {
-      pendingReflectiveUses.add(currentMethod);
+      pendingReflectiveUses.add(context.method);
     } else if (appView.dexItemFactory().classMethods.isReflectiveMemberLookup(invokedMethod)) {
       // Implicitly add -identifiernamestring rule for the Java reflection in use.
       identifierNameStrings.add(invokedMethod);
       // Revisit the current method to implicitly add -keep rule for items with reflective access.
-      pendingReflectiveUses.add(currentMethod);
+      pendingReflectiveUses.add(context.method);
     }
-    if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, currentMethod)) {
+    if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context.method)) {
       return false;
     }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Register invokeVirtual `%s`.", invokedMethod);
     }
-    workList.enqueueMarkReachableVirtualAction(invokedMethod, reason);
+    markVirtualMethodAsReachable(invokedMethod, false, context, reason);
     return true;
   }
 
-  boolean traceNewInstance(DexType type, DexEncodedMethod currentMethod) {
+  boolean traceNewInstance(DexType type, ProgramMethod context) {
+    DexEncodedMethod currentMethod = context.method;
     boolean skipTracing =
         registerDeferredActionForDeadProtoBuilder(
-            type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, currentMethod));
+            type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context));
     if (skipTracing) {
       return false;
     }
 
-    return traceNewInstance(type, currentMethod, KeepReason.instantiatedIn(currentMethod));
+    return traceNewInstance(type, context, KeepReason.instantiatedIn(currentMethod));
   }
 
-  boolean traceNewInstanceFromLambda(DexType type, DexEncodedMethod currentMethod) {
-    return traceNewInstance(
-        type, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+  boolean traceNewInstanceFromLambda(DexType type, ProgramMethod context) {
+    return traceNewInstance(type, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
   }
 
-  private boolean traceNewInstance(
-      DexType type, DexEncodedMethod currentMethod, KeepReason keepReason) {
+  private boolean traceNewInstance(DexType type, ProgramMethod context, KeepReason keepReason) {
+    DexEncodedMethod currentMethod = context.method;
     DexProgramClass clazz = getProgramClassOrNull(type);
     if (clazz != null) {
       if (clazz.isInterface()) {
@@ -1960,16 +1962,8 @@
     }
   }
 
-  // Package protected due to entry point from worklist.
-  void markVirtualMethodAsReachable(DexMethod method, boolean interfaceInvoke, KeepReason reason) {
-    markVirtualMethodAsReachable(method, interfaceInvoke, reason, (x, y) -> true);
-  }
-
   private void markVirtualMethodAsReachable(
-      DexMethod method,
-      boolean interfaceInvoke,
-      KeepReason reason,
-      BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter) {
+      DexMethod method, boolean interfaceInvoke, ProgramMethod contextOrNull, KeepReason reason) {
     if (method.holder.isArrayType()) {
       // This is an array type, so the actual class will be generated at runtime. We treat this
       // like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
@@ -2002,6 +1996,16 @@
 
     // Otherwise, the resolution target is marked and cached, and all possible targets identified.
     resolution = findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+    if (contextOrNull != null
+        && !resolution.isUnresolved()
+        && !AccessControl.isMethodAccessible(
+            resolution.method, resolution.holder, contextOrNull.holder, appInfo)) {
+      // Not accessible from this context, so this call will cause a runtime exception.
+      // Note that the resolution is not cached, as another call context may be valid.
+      return;
+    }
+
+    // The resolution is unresolved or accessible, both are context independent, so cache it.
     virtualTargetsMarkedAsReachable.put(method, resolution);
     if (resolution.isUnresolved() || !resolution.method.isVirtualMethod()) {
       // There is no valid resolution, so any call will lead to a runtime exception.
@@ -2026,13 +2030,12 @@
       if (encodedPossibleTarget.isAbstract()) {
         continue;
       }
-      markPossibleTargetsAsReachable(resolution, possibleTargetsFilter, encodedPossibleTarget);
+      markPossibleTargetsAsReachable(resolution, encodedPossibleTarget);
     }
   }
 
   private void markPossibleTargetsAsReachable(
       MarkedResolutionTarget reason,
-      BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter,
       DexEncodedMethod encodedPossibleTarget) {
     assert encodedPossibleTarget.isVirtualMethod();
     assert !encodedPossibleTarget.isAbstract();
@@ -2041,9 +2044,6 @@
     if (clazz == null) {
       return;
     }
-    if (!possibleTargetsFilter.test(clazz, encodedPossibleTarget)) {
-      return;
-    }
     ReachableVirtualMethodsSet reachable =
         reachableVirtualMethods.computeIfAbsent(clazz, ignore -> new ReachableVirtualMethodsSet());
     if (!reachable.add(encodedPossibleTarget, reason)) {
@@ -2499,10 +2499,8 @@
       // A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
       // their overrides. However, we don't mark it live, as a keep rule might not imply that
       // the corresponding class is live.
-      if (!holder.isInterface()) {
-        workList.enqueueMarkReachableVirtualAction(method, reason);
-      } else {
-        workList.enqueueMarkReachableInterfaceAction(method, reason);
+      markVirtualMethodAsReachable(method, holder.isInterface(), null, reason);
+      if (holder.isInterface()) {
         // Reachability for default methods is based on live subtypes in general. For keep rules,
         // we need special handling as we essentially might have live subtypes that are outside of
         // the current compilation unit. Keep either the default-method or its implementation
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index ff6ece0..d98d7c9 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -11,6 +11,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.shaking.GraphReporter.KeepReasonWitness;
 import java.util.ArrayDeque;
 import java.util.Queue;
@@ -36,36 +37,6 @@
     }
   }
 
-  static class MarkReachableVirtualAction extends EnqueuerAction {
-    final DexMethod target;
-    final KeepReason reason;
-
-    MarkReachableVirtualAction(DexMethod target, KeepReason reason) {
-      this.target = target;
-      this.reason = reason;
-    }
-
-    @Override
-    public void run(Enqueuer enqueuer) {
-      enqueuer.markVirtualMethodAsReachable(target, false, reason);
-    }
-  }
-
-  static class MarkReachableInterfaceAction extends EnqueuerAction {
-    final DexMethod target;
-    final KeepReason reason;
-
-    public MarkReachableInterfaceAction(DexMethod target, KeepReason reason) {
-      this.target = target;
-      this.reason = reason;
-    }
-
-    @Override
-    public void run(Enqueuer enqueuer) {
-      enqueuer.markVirtualMethodAsReachable(target, true, reason);
-    }
-  }
-
   static class MarkReachableSuperAction extends EnqueuerAction {
     final DexMethod target;
     final DexEncodedMethod context;
@@ -194,22 +165,22 @@
 
     @Override
     public void run(Enqueuer enqueuer) {
-      enqueuer.traceInvokeDirect(invokedMethod, currentHolder, currentMethod);
+      enqueuer.traceInvokeDirect(invokedMethod, new ProgramMethod(currentHolder, currentMethod));
     }
   }
 
   static class TraceNewInstanceAction extends EnqueuerAction {
     final DexType type;
-    final DexEncodedMethod currentMethod;
+    final ProgramMethod context;
 
-    TraceNewInstanceAction(DexType type, DexEncodedMethod currentMethod) {
+    TraceNewInstanceAction(DexType type, ProgramMethod context) {
       this.type = type;
-      this.currentMethod = currentMethod;
+      this.context = context;
     }
 
     @Override
     public void run(Enqueuer enqueuer) {
-      enqueuer.traceNewInstance(type, currentMethod);
+      enqueuer.traceNewInstance(type, context);
     }
   }
 
@@ -251,14 +222,6 @@
     queue.add(new MarkReachableDirectAction(method, reason));
   }
 
-  void enqueueMarkReachableVirtualAction(DexMethod method, KeepReason reason) {
-    queue.add(new MarkReachableVirtualAction(method, reason));
-  }
-
-  void enqueueMarkReachableInterfaceAction(DexMethod method, KeepReason reason) {
-    queue.add(new MarkReachableInterfaceAction(method, reason));
-  }
-
   void enqueueMarkReachableSuperAction(DexMethod method, DexEncodedMethod from) {
     queue.add(new MarkReachableSuperAction(method, from));
   }
@@ -306,9 +269,8 @@
     queue.add(new TraceInvokeDirectAction(invokedMethod, currentHolder, currentMethod));
   }
 
-  public void enqueueTraceNewInstanceAction(DexType type, DexEncodedMethod currentMethod) {
-    assert currentMethod.isProgramMethod(appView);
-    queue.add(new TraceNewInstanceAction(type, currentMethod));
+  public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
+    queue.add(new TraceNewInstanceAction(type, context));
   }
 
   public void enqueueTraceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) {