Move tracking of methods targeted by invoke-dynamic to keep info

Bug: 218986437
Change-Id: Idcb5121e4d5992a1d35f891d3ad783e0f91f1b68
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index d7dc429..78a8743 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -41,6 +41,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * The class merger is responsible for moving methods from the sources in {@link ClassMerger#group}
@@ -195,15 +196,22 @@
 
   void mergeMethods(
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
-    mergeVirtualMethods();
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
+    mergeVirtualMethods(virtuallyMergedMethodsKeepInfoConsumer);
     mergeDirectMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
     classMethodsBuilder.setClassMethods(group.getTarget());
   }
 
-  void mergeVirtualMethods() {
+  void mergeVirtualMethods(
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     virtualMethodMergers.forEach(
-        merger -> merger.merge(classMethodsBuilder, lensBuilder, classIdentifiers));
+        merger ->
+            merger.merge(
+                classMethodsBuilder,
+                lensBuilder,
+                classIdentifiers,
+                virtuallyMergedMethodsKeepInfoConsumer));
     group.forEachSource(clazz -> clazz.getMethodCollection().clearVirtualMethods());
   }
 
@@ -318,13 +326,17 @@
   public void mergeGroup(
       PrunedItems.Builder prunedItemsBuilder,
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     fixAccessFlags();
     fixNestMemberAttributes();
     mergeAnnotations();
     mergeInterfaces();
     mergeFields(prunedItemsBuilder);
-    mergeMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
+    mergeMethods(
+        syntheticArgumentClass,
+        syntheticInitializerConverterBuilder,
+        virtuallyMergedMethodsKeepInfoConsumer);
     group.getTarget().clearClassSignature();
     group.getTarget().forEachProgramMember(ProgramMember::clearGenericSignature);
     group.forEachSource(clazz -> prunedItemsBuilder.addRemovedClass(clazz.getType()));
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 18872a7..dd7de45 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -4,12 +4,18 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
+import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -24,6 +30,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
 public class HorizontalClassMerger {
 
@@ -104,9 +111,13 @@
             : null;
     SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
         SyntheticInitializerConverter.builder(appView, codeProvider);
+    List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos = new ArrayList<>();
     PrunedItems prunedItems =
         applyClassMergers(
-            classMergers, syntheticArgumentClass, syntheticInitializerConverterBuilder);
+            classMergers,
+            syntheticArgumentClass,
+            syntheticInitializerConverterBuilder,
+            virtuallyMergedMethodsKeepInfos::add);
 
     SyntheticInitializerConverter syntheticInitializerConverter =
         syntheticInitializerConverterBuilder.build();
@@ -144,6 +155,37 @@
 
     appView.pruneItems(
         prunedItems.toBuilder().setPrunedApp(appView.app()).build(), executorService);
+
+    amendKeepInfo(horizontalClassMergerGraphLens, virtuallyMergedMethodsKeepInfos);
+  }
+
+  private void amendKeepInfo(
+      HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
+      List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos) {
+    appView
+        .getKeepInfo()
+        .mutate(
+            keepInfo -> {
+              for (VirtuallyMergedMethodsKeepInfo virtuallyMergedMethodsKeepInfo :
+                  virtuallyMergedMethodsKeepInfos) {
+                DexMethod representative = virtuallyMergedMethodsKeepInfo.getRepresentative();
+                MethodLookupResult lookupResult =
+                    horizontalClassMergerGraphLens.lookupMethod(
+                        representative,
+                        null,
+                        Type.VIRTUAL,
+                        horizontalClassMergerGraphLens.getPrevious());
+                ProgramMethod mergedMethod =
+                    asProgramMethodOrNull(appView.definitionFor(lookupResult.getReference()));
+                if (mergedMethod != null) {
+                  keepInfo.joinMethod(
+                      mergedMethod,
+                      info -> info.merge(virtuallyMergedMethodsKeepInfo.getKeepInfo()));
+                  continue;
+                }
+                assert false;
+              }
+            });
   }
 
   private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
@@ -245,11 +287,15 @@
   private PrunedItems applyClassMergers(
       Collection<ClassMerger> classMergers,
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
     for (ClassMerger merger : classMergers) {
       merger.mergeGroup(
-          prunedItemsBuilder, syntheticArgumentClass, syntheticInitializerConverterBuilder);
+          prunedItemsBuilder,
+          syntheticArgumentClass,
+          syntheticInitializerConverterBuilder,
+          virtuallyMergedMethodsKeepInfoConsumer);
     }
     return prunedItemsBuilder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 07d1fd0..67cc625 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -23,6 +23,7 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class VirtualMethodMerger {
 
@@ -224,7 +225,8 @@
   public void merge(
       ClassMethodsBuilder classMethodsBuilder,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Reference2IntMap<DexType> classIdentifiers) {
+      Reference2IntMap<DexType> classIdentifiers,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     assert !methods.isEmpty();
 
     // Handle trivial merges.
@@ -284,13 +286,20 @@
     }
 
     // Map each old non-abstract method to the newly synthesized method in the graph lens.
+    VirtuallyMergedMethodsKeepInfo virtuallyMergedMethodsKeepInfo =
+        new VirtuallyMergedMethodsKeepInfo(representative.getReference());
     for (ProgramMethod oldMethod : methods) {
       lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
+      virtuallyMergedMethodsKeepInfo.amendKeepInfo(appView.getKeepInfo(oldMethod));
     }
 
     // Add a mapping from a synthetic name to the synthetic merged method.
     lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
 
     classMethodsBuilder.addVirtualMethod(newMethod);
+
+    if (!virtuallyMergedMethodsKeepInfo.getKeepInfo().isBottom()) {
+      virtuallyMergedMethodsKeepInfoConsumer.accept(virtuallyMergedMethodsKeepInfo);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java
new file mode 100644
index 0000000..b395d5d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
+
+public class VirtuallyMergedMethodsKeepInfo {
+
+  private final DexMethod representative;
+  private final KeepMethodInfo.Joiner keepInfo = KeepMethodInfo.newEmptyJoiner();
+
+  public VirtuallyMergedMethodsKeepInfo(DexMethod representative) {
+    this.representative = representative;
+  }
+
+  public void amendKeepInfo(KeepMethodInfo keepInfo) {
+    this.keepInfo.merge(keepInfo.joiner());
+  }
+
+  public DexMethod getRepresentative() {
+    return representative;
+  }
+
+  public Joiner getKeepInfo() {
+    return keepInfo;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index a46a6bc..125b53c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.classhierarchy.MethodOverridesCollector;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
@@ -57,14 +59,17 @@
     private final Set<DexMethod> multiCallerInlineCandidates = Sets.newIdentityHashSet();
 
     CallGraphBasedCallSiteInformation(AppView<AppInfoWithLiveness> appView, CallGraph graph) {
+      InternalOptions options = appView.options();
       ProgramMethodSet pinned =
           MethodOverridesCollector.findAllMethodsAndOverridesThatMatches(
               appView,
               ImmediateProgramSubtypingInfo.create(appView),
               appView.appInfo().classes(),
-              method ->
-                  appView.getKeepInfo(method).isPinned(appView.options())
-                      || appView.appInfo().isMethodTargetedByInvokeDynamic(method));
+              method -> {
+                KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+                return !keepInfo.isClosedWorldReasoningAllowed(options)
+                    || keepInfo.isPinned(options);
+              });
 
       for (Node node : graph.getNodes()) {
         ProgramMethod method = node.getProgramMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
deleted file mode 100644
index 8f9421d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class ArgumentRemovalUtils {
-
-  // Returns true if this method is pinned from the perspective of optimizations that attempt to
-  // remove method arguments.
-  public static boolean isPinned(DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
-    return appView.appInfo().isPinned(method.getReference())
-        || appView.appInfo().isBootstrapMethod(method.getReference())
-        || appView.appInfo().isFailedResolutionTarget(method.getReference())
-        || appView.appInfo().isMethodTargetedByInvokeDynamic(method.getReference())
-        || method.accessFlags.isNative();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 5517455..24d6e46 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -420,7 +420,6 @@
       return appView.getKeepInfo(method).isParameterRemovalAllowed(options)
           && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
           && !appView.appInfo().isBootstrapMethod(method)
-          && !appView.appInfo().isMethodTargetedByInvokeDynamic(method)
           && !interfaceDispatchOutsideProgram.contains(method);
     }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
index c277bb7..28aa563 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
@@ -67,7 +67,6 @@
     AppInfoWithLiveness appInfo = appView.appInfo();
     InternalOptions options = appView.options();
     return method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
-        || appInfo.isMethodTargetedByInvokeDynamic(method)
         || !appInfo.getKeepInfo().getMethodInfo(method).isArgumentPropagationAllowed(options);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
index 34d8967..cb44578 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -22,8 +22,7 @@
     if (!appView.getKeepInfo(method).isUnusedArgumentOptimizationAllowed(options)) {
       return false;
     }
-    return method.getDefinition().isLibraryMethodOverride().isFalse()
-        && !appView.appInfoWithLiveness().isMethodTargetedByInvokeDynamic(method);
+    return method.getDefinition().isLibraryMethodOverride().isFalse();
   }
 
   public static boolean canRemoveUnusedParameter(
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 8216777..7464171 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -112,8 +112,6 @@
    */
   private final Set<DexMethod> bootstrapMethods;
 
-  /** Set of methods that are the immediate target of an invoke-dynamic. */
-  private final Set<DexMethod> methodsTargetedByInvokeDynamic;
   /** Set of virtual methods that are the immediate target of an invoke-direct. */
   private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect;
   /**
@@ -210,7 +208,6 @@
       Set<DexMethod> failedMethodResolutionTargets,
       Set<DexField> failedFieldResolutionTargets,
       Set<DexMethod> bootstrapMethods,
-      Set<DexMethod> methodsTargetedByInvokeDynamic,
       Set<DexMethod> virtualMethodsTargetedByInvokeDirect,
       Set<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
@@ -245,7 +242,6 @@
     this.failedMethodResolutionTargets = failedMethodResolutionTargets;
     this.failedFieldResolutionTargets = failedFieldResolutionTargets;
     this.bootstrapMethods = bootstrapMethods;
-    this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
@@ -288,7 +284,6 @@
         previous.failedMethodResolutionTargets,
         previous.failedFieldResolutionTargets,
         previous.bootstrapMethods,
-        previous.methodsTargetedByInvokeDynamic,
         previous.virtualMethodsTargetedByInvokeDirect,
         previous.liveMethods,
         previous.fieldAccessInfoCollection,
@@ -335,8 +330,6 @@
         pruneFields(previous.failedFieldResolutionTargets, prunedItems, executorService, futures),
         pruneMethods(previous.bootstrapMethods, prunedItems, executorService, futures),
         pruneMethods(
-            previous.methodsTargetedByInvokeDynamic, prunedItems, executorService, futures),
-        pruneMethods(
             previous.virtualMethodsTargetedByInvokeDirect, prunedItems, executorService, futures),
         pruneMethods(previous.liveMethods, prunedItems, executorService, futures),
         previous.fieldAccessInfoCollection,
@@ -541,7 +534,6 @@
         failedMethodResolutionTargets,
         failedFieldResolutionTargets,
         bootstrapMethods,
-        methodsTargetedByInvokeDynamic,
         virtualMethodsTargetedByInvokeDirect,
         liveMethods,
         fieldAccessInfoCollection,
@@ -621,7 +613,6 @@
     this.failedMethodResolutionTargets = previous.failedMethodResolutionTargets;
     this.failedFieldResolutionTargets = previous.failedFieldResolutionTargets;
     this.bootstrapMethods = previous.bootstrapMethods;
-    this.methodsTargetedByInvokeDynamic = previous.methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = previous.liveMethods;
     this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
@@ -745,14 +736,6 @@
     return isBootstrapMethod(method.getReference());
   }
 
-  public boolean isMethodTargetedByInvokeDynamic(DexMethod method) {
-    return methodsTargetedByInvokeDynamic.contains(method);
-  }
-
-  public boolean isMethodTargetedByInvokeDynamic(ProgramMethod method) {
-    return isMethodTargetedByInvokeDynamic(method.getReference());
-  }
-
   public Set<DexMethod> getVirtualMethodsTargetedByInvokeDirect() {
     return virtualMethodsTargetedByInvokeDirect;
   }
@@ -1232,7 +1215,6 @@
         lens.rewriteReferences(failedMethodResolutionTargets),
         lens.rewriteReferences(failedFieldResolutionTargets),
         lens.rewriteReferences(bootstrapMethods),
-        lens.rewriteReferences(methodsTargetedByInvokeDynamic),
         lens.rewriteReferences(virtualMethodsTargetedByInvokeDirect),
         lens.rewriteReferences(liveMethods),
         fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
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 941e354..1125b65 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -355,10 +355,6 @@
    */
   private final Set<DexMethod> bootstrapMethods = Sets.newIdentityHashSet();
   /**
-   * Set of direct methods that are the immediate target of an invoke-dynamic.
-   */
-  private final Set<DexMethod> methodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
-  /**
    * Set of virtual methods that are the immediate target of an invoke-direct.
    */
   private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
@@ -1030,7 +1026,7 @@
         if (bootstrapArgument.isDexValueMethodHandle()) {
           DexMethodHandle method = bootstrapArgument.asDexValueMethodHandle().getValue();
           if (method.isMethodHandle()) {
-            methodsTargetedByInvokeDynamic.add(method.asMethod());
+            disableClosedWorldReasoning(method.asMethod(), context);
           }
         }
       }
@@ -1051,10 +1047,6 @@
     assert implHandle != null;
 
     DexMethod method = implHandle.asMethod();
-    if (!methodsTargetedByInvokeDynamic.add(method)) {
-      return;
-    }
-
     switch (implHandle.type) {
       case INVOKE_STATIC:
         traceInvokeStaticFromLambda(method, context);
@@ -1074,6 +1066,18 @@
       default:
         throw new Unreachable();
     }
+
+    disableClosedWorldReasoning(method, context);
+  }
+
+  private void disableClosedWorldReasoning(DexMethod reference, ProgramMethod context) {
+    SingleResolutionResult resolutionResult =
+        resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context));
+    if (resolutionResult != null && resolutionResult.getResolvedHolder().isProgramClass()) {
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          resolutionResult.getResolvedProgramMethod(),
+          KeepMethodInfo.newEmptyJoiner().disallowClosedWorldReasoning());
+    }
   }
 
   void traceCheckCast(DexType type, ProgramMethod currentMethod, boolean ignoreCompatRules) {
@@ -3839,7 +3843,6 @@
             failedMethodResolutionTargets,
             failedFieldResolutionTargets,
             bootstrapMethods,
-            methodsTargetedByInvokeDynamic,
             virtualMethodsTargetedByInvokeDirect,
             toDescriptorSet(liveMethods.getItems()),
             // Filter out library fields and pinned fields, because these are read by default.
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index cd46a71..0173fb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -25,6 +25,7 @@
   }
 
   private final boolean allowClassInlining;
+  private final boolean allowClosedWorldReasoning;
   private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
@@ -37,6 +38,7 @@
   private KeepMethodInfo(Builder builder) {
     super(builder);
     this.allowClassInlining = builder.isClassInliningAllowed();
+    this.allowClosedWorldReasoning = builder.isClosedWorldReasoningAllowed();
     this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
@@ -59,7 +61,8 @@
   }
 
   public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && !isCheckDiscardedEnabled(configuration);
   }
@@ -72,6 +75,14 @@
     return allowClassInlining;
   }
 
+  public boolean isClosedWorldReasoningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsClosedWorldReasoningAllowed();
+  }
+
+  boolean internalIsClosedWorldReasoningAllowed() {
+    return allowClosedWorldReasoning;
+  }
+
   public boolean isConstantArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsConstantArgumentOptimizationAllowed();
   }
@@ -89,7 +100,8 @@
   }
 
   public boolean isMethodStaticizingAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && configuration.isMethodStaticizingEnabled()
         && internalIsMethodStaticizingAllowed();
@@ -100,7 +112,8 @@
   }
 
   public boolean isParameterReorderingAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsParameterReorderingAllowed();
   }
@@ -110,7 +123,8 @@
   }
 
   public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsParameterTypeStrengtheningAllowed();
   }
@@ -120,7 +134,8 @@
   }
 
   public boolean isReturnTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsReturnTypeStrengtheningAllowed();
   }
@@ -130,7 +145,8 @@
   }
 
   public boolean isUnusedArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsUnusedArgumentOptimizationAllowed();
   }
@@ -140,7 +156,8 @@
   }
 
   public boolean isUnusedReturnValueOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsUnusedReturnValueOptimizationAllowed();
   }
@@ -167,6 +184,7 @@
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private boolean allowClassInlining;
+    private boolean allowClosedWorldReasoning;
     private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
@@ -183,6 +201,7 @@
     private Builder(KeepMethodInfo original) {
       super(original);
       allowClassInlining = original.internalIsClassInliningAllowed();
+      allowClosedWorldReasoning = original.internalIsClosedWorldReasoningAllowed();
       allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
@@ -213,6 +232,25 @@
       return setAllowClassInlining(false);
     }
 
+    // Closed world reasoning.
+
+    public boolean isClosedWorldReasoningAllowed() {
+      return allowClosedWorldReasoning;
+    }
+
+    public Builder setAllowClosedWorldReasoning(boolean allowClosedWorldReasoning) {
+      this.allowClosedWorldReasoning = allowClosedWorldReasoning;
+      return self();
+    }
+
+    public Builder allowClosedWorldReasoning() {
+      return setAllowClosedWorldReasoning(true);
+    }
+
+    public Builder disallowClosedWorldReasoning() {
+      return setAllowClosedWorldReasoning(false);
+    }
+
     // Constant argument optimization.
 
     public boolean isConstantArgumentOptimizationAllowed() {
@@ -390,6 +428,7 @@
     boolean internalIsEqualTo(KeepMethodInfo other) {
       return super.internalIsEqualTo(other)
           && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+          && isClosedWorldReasoningAllowed() == other.internalIsClosedWorldReasoningAllowed()
           && isConstantArgumentOptimizationAllowed()
               == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
@@ -413,6 +452,7 @@
     public Builder makeTop() {
       return super.makeTop()
           .disallowClassInlining()
+          .disallowClosedWorldReasoning()
           .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
@@ -427,6 +467,7 @@
     public Builder makeBottom() {
       return super.makeBottom()
           .allowClassInlining()
+          .allowClosedWorldReasoning()
           .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
@@ -449,6 +490,11 @@
       return self();
     }
 
+    public Joiner disallowClosedWorldReasoning() {
+      builder.disallowClosedWorldReasoning();
+      return self();
+    }
+
     public Joiner disallowConstantArgumentOptimization() {
       builder.disallowConstantArgumentOptimization();
       return self();
@@ -500,6 +546,8 @@
       return super.merge(joiner)
           .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
           .applyIf(
+              !joiner.builder.isClosedWorldReasoningAllowed(), Joiner::disallowClosedWorldReasoning)
+          .applyIf(
               !joiner.builder.isConstantArgumentOptimizationAllowed(),
               Joiner::disallowConstantArgumentOptimization)
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)