Refactor pruned items removal from AppInfoWithLiveness

Change-Id: I6c16255b64f3267805d468a97eeb0e17ae70317c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index cfb3540..1dc0468 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
 import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis;
@@ -419,8 +420,12 @@
 
           // Recompute the subtyping information.
           Set<DexType> removedClasses = pruner.getRemovedClasses();
-          appView.removePrunedClasses(
-              prunedApp, removedClasses, pruner.getMethodsToKeepForConfigurationDebugging());
+          appView.pruneItems(
+              PrunedItems.builder()
+                  .setPrunedApp(prunedApp)
+                  .addRemovedClasses(removedClasses)
+                  .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging())
+                  .build());
           new AbstractMethodRemover(
                   appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo())
               .run();
@@ -587,7 +592,12 @@
               merger.run(appBuilder, mainDexTracingResult, runtimeTypeCheckInfo);
           if (lens != null) {
             DirectMappedDexApplication app = appBuilder.build();
-            appView.removePrunedClasses(app, appView.horizontallyMergedClasses().getSources());
+            appView.pruneItems(
+                PrunedItems.builder()
+                    .setPrunedApp(app)
+                    .addRemovedClasses(appView.horizontallyMergedClasses().getSources())
+                    .addNoLongerSyntheticItems(appView.horizontallyMergedClasses().getTargets())
+                    .build());
             appView.rewriteWithLens(lens);
 
             // Only required for class merging, clear instance to save memory.
@@ -748,10 +758,12 @@
                   options.reporter, options.usageInformationConsumer);
             }
 
-            appView.removePrunedClasses(
-                application,
-                CollectionUtils.mergeSets(prunedTypes, removedClasses),
-                pruner.getMethodsToKeepForConfigurationDebugging());
+            appView.pruneItems(
+                PrunedItems.builder()
+                    .setPrunedApp(application)
+                    .addRemovedClasses(CollectionUtils.mergeSets(prunedTypes, removedClasses))
+                    .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging())
+                    .build());
 
             new BridgeHoisting(appViewWithLiveness).run();
 
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index b6588df..ec95ade 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.Sets;
@@ -135,11 +136,11 @@
     return rewrittenClassToFeatureSplitMap;
   }
 
-  public ClassToFeatureSplitMap withoutPrunedClasses(Set<DexType> prunedClasses) {
+  public ClassToFeatureSplitMap withoutPrunedItems(PrunedItems prunedItems) {
     ClassToFeatureSplitMap classToFeatureSplitMapAfterPruning = new ClassToFeatureSplitMap();
     classToFeatureSplitMap.forEach(
         (type, featureSplit) -> {
-          if (!prunedClasses.contains(type)) {
+          if (!prunedItems.getRemovedClasses().contains(type)) {
             classToFeatureSplitMapAfterPruning.classToFeatureSplitMap.put(type, featureSplit);
           }
         });
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index 7fe273a..c0c3976 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -26,7 +26,6 @@
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -139,11 +138,11 @@
     return new AppServices(appView, rewrittenFeatureMappings.build());
   }
 
-  public AppServices prunedCopy(Collection<DexType> removedClasses) {
+  public AppServices prunedCopy(PrunedItems prunedItems) {
     ImmutableMap.Builder<DexType, Map<FeatureSplit, List<DexType>>> rewrittenServicesBuilder =
         ImmutableMap.builder();
     for (Entry<DexType, Map<FeatureSplit, List<DexType>>> entry : services.entrySet()) {
-      if (removedClasses.contains(entry.getKey())) {
+      if (prunedItems.getRemovedClasses().contains(entry.getKey())) {
         continue;
       }
       ImmutableMap.Builder<FeatureSplit, List<DexType>> prunedFeatureSplitImpls =
@@ -152,7 +151,7 @@
         ImmutableList.Builder<DexType> rewrittenServiceImplementationTypesBuilder =
             ImmutableList.builder();
         for (DexType serviceImplementationType : featureSplitEntry.getValue()) {
-          if (!removedClasses.contains(serviceImplementationType)) {
+          if (!prunedItems.getRemovedClasses().contains(serviceImplementationType)) {
             rewrittenServiceImplementationTypesBuilder.add(serviceImplementationType);
           }
         }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index ce2d937..d4728a4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -38,8 +38,6 @@
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSet;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -569,35 +567,17 @@
     return !cfByteCodePassThrough.isEmpty();
   }
 
-  public void removePrunedClasses(
-      DirectMappedDexApplication prunedApp, Set<DexType> removedClasses) {
-    removePrunedClasses(prunedApp, removedClasses, Collections.emptySet());
+  public void pruneItems(PrunedItems prunedItems) {
+    pruneItems(withLiveness(), prunedItems);
   }
 
-  public void removePrunedClasses(
-      DirectMappedDexApplication prunedApp,
-      Set<DexType> removedClasses,
-      Collection<DexMethod> methodsToKeepForConfigurationDebugging) {
-    assert enableWholeProgramOptimizations();
-    assert appInfo().hasLiveness();
-    removePrunedClasses(
-        prunedApp, removedClasses, methodsToKeepForConfigurationDebugging, withLiveness());
-  }
-
-  private static void removePrunedClasses(
-      DirectMappedDexApplication prunedApp,
-      Set<DexType> removedClasses,
-      Collection<DexMethod> methodsToKeepForConfigurationDebugging,
-      AppView<AppInfoWithLiveness> appView) {
-    if (removedClasses.isEmpty() && !appView.options().configurationDebugging) {
-      assert appView.appInfo.app() == prunedApp;
+  private static void pruneItems(AppView<AppInfoWithLiveness> appView, PrunedItems prunedItems) {
+    if (!prunedItems.hasRemovedClasses() && !appView.options().configurationDebugging) {
+      assert appView.appInfo().app() == prunedItems.getPrunedApp();
       return;
     }
-    appView.setAppInfo(
-        appView
-            .appInfo()
-            .prunedCopyFrom(prunedApp, removedClasses, methodsToKeepForConfigurationDebugging));
-    appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
+    appView.setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems));
+    appView.setAppServices(appView.appServices().prunedCopy(prunedItems));
   }
 
   public void rewriteWithLens(NonIdentityGraphLens lens) {
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
new file mode 100644
index 0000000..2df596c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Set;
+
+public class PrunedItems {
+
+  private final DexApplication prunedApp;
+  private final Set<DexReference> additionalPinnedItems;
+  private final Set<DexType> noLongerSyntheticItems;
+  private final Set<DexType> removedClasses;
+
+  private PrunedItems(
+      DexApplication prunedApp,
+      Set<DexReference> additionalPinnedItems,
+      Set<DexType> noLongerSyntheticItems,
+      Set<DexType> removedClasses) {
+    this.prunedApp = prunedApp;
+    this.additionalPinnedItems = additionalPinnedItems;
+    this.noLongerSyntheticItems = noLongerSyntheticItems;
+    this.removedClasses = removedClasses;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static PrunedItems empty(DexApplication application) {
+    return new Builder().setPrunedApp(application).build();
+  }
+
+  public DexApplication getPrunedApp() {
+    return prunedApp;
+  }
+
+  public Set<? extends DexReference> getAdditionalPinnedItems() {
+    return additionalPinnedItems;
+  }
+
+  public Set<DexType> getNoLongerSyntheticItems() {
+    return noLongerSyntheticItems;
+  }
+
+  public boolean hasRemovedClasses() {
+    return !removedClasses.isEmpty();
+  }
+
+  public Set<DexType> getRemovedClasses() {
+    return removedClasses;
+  }
+
+  public static class Builder {
+
+    private DexApplication prunedApp;
+
+    private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet();
+    private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet();
+    private final Set<DexType> removedClasses = Sets.newIdentityHashSet();
+
+    public Builder setPrunedApp(DexApplication prunedApp) {
+      this.prunedApp = prunedApp;
+      return this;
+    }
+
+    public Builder addAdditionalPinnedItems(
+        Collection<? extends DexReference> additionalPinnedItems) {
+      this.additionalPinnedItems.addAll(additionalPinnedItems);
+      return this;
+    }
+
+    public Builder addNoLongerSyntheticItems(Set<DexType> noLongerSyntheticItems) {
+      this.noLongerSyntheticItems.addAll(noLongerSyntheticItems);
+      return this;
+    }
+
+    public Builder addRemovedClasses(Set<DexType> removedClasses) {
+      this.noLongerSyntheticItems.addAll(removedClasses);
+      this.removedClasses.addAll(removedClasses);
+      return this;
+    }
+
+    public PrunedItems build() {
+      return new PrunedItems(
+          prunedApp, additionalPinnedItems, noLongerSyntheticItems, removedClasses);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index ff108a4..b9195c3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -45,6 +45,10 @@
     return mergedClasses.getKeys(type);
   }
 
+  public Set<DexType> getTargets() {
+    return mergedClasses.values();
+  }
+
   @Override
   public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return mergedClasses.containsKey(type);
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 c5adef8..c1275c5 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -318,20 +319,16 @@
         previous.initClassReferences);
   }
 
-  private AppInfoWithLiveness(
-      AppInfoWithLiveness previous,
-      DirectMappedDexApplication application,
-      Set<DexType> removedClasses,
-      Collection<? extends DexReference> additionalPinnedItems) {
+  private AppInfoWithLiveness(AppInfoWithLiveness previous, PrunedItems prunedItems) {
     this(
-        previous.getSyntheticItems().commitPrunedClasses(application, removedClasses),
-        previous.getClassToFeatureSplitMap().withoutPrunedClasses(removedClasses),
-        previous.getMainDexClasses().withoutPrunedClasses(removedClasses),
+        previous.getSyntheticItems().commitPrunedItems(prunedItems),
+        previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
+        previous.getMainDexClasses().withoutPrunedItems(prunedItems),
         previous.deadProtoTypes,
         previous.missingTypes,
-        removedClasses == null
-            ? previous.liveTypes
-            : Sets.difference(previous.liveTypes, removedClasses),
+        prunedItems.hasRemovedClasses()
+            ? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses())
+            : previous.liveTypes,
         previous.targetedMethods,
         previous.failedResolutionTargets,
         previous.bootstrapMethods,
@@ -342,7 +339,7 @@
         previous.methodAccessInfoCollection,
         previous.objectAllocationInfoCollection,
         previous.callSites,
-        extendPinnedItems(previous, additionalPinnedItems),
+        extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
         previous.mayHaveSideEffects,
         previous.noSideEffects,
         previous.assumedValues,
@@ -362,14 +359,14 @@
         previous.noStaticClassMerging,
         previous.neverPropagateValue,
         previous.identifierNameStrings,
-        removedClasses == null
-            ? previous.prunedTypes
-            : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses),
+        prunedItems.hasRemovedClasses()
+            ? CollectionUtils.mergeSets(previous.prunedTypes, prunedItems.getRemovedClasses())
+            : previous.prunedTypes,
         previous.switchMaps,
         previous.enumValueInfoMaps,
         previous.lockCandidates,
         previous.initClassReferences);
-    assert keepInfo.verifyNoneArePinned(removedClasses, previous);
+    assert keepInfo.verifyNoneArePinned(prunedItems.getRemovedClasses(), previous);
   }
 
   private static KeepInfoCollection extendPinnedItems(
@@ -940,17 +937,15 @@
    * Returns a copy of this AppInfoWithLiveness where the set of classes is pruned using the given
    * DexApplication object.
    */
-  public AppInfoWithLiveness prunedCopyFrom(
-      DirectMappedDexApplication application,
-      Set<DexType> removedClasses,
-      Collection<? extends DexReference> additionalPinnedItems) {
+  public AppInfoWithLiveness prunedCopyFrom(PrunedItems prunedItems) {
     assert checkIfObsolete();
-    if (!removedClasses.isEmpty()) {
+    if (!prunedItems.hasRemovedClasses()) {
       // Rebuild the hierarchy.
       objectAllocationInfoCollection.mutate(mutator -> {}, this);
-      keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(removedClasses));
+      keepInfo.mutate(
+          keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedClasses()));
     }
-    return new AppInfoWithLiveness(this, application, removedClasses, additionalPinnedItems);
+    return new AppInfoWithLiveness(this, prunedItems);
   }
 
   public AppInfoWithLiveness rebuildWithLiveness(
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
index a5fbd32..4e6b632 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
 import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -87,10 +88,10 @@
     return mainDexClasses.size();
   }
 
-  public MainDexClasses withoutPrunedClasses(Set<DexType> prunedClasses) {
+  public MainDexClasses withoutPrunedItems(PrunedItems prunedItems) {
     MainDexClasses mainDexClassesAfterPruning = createEmptyMainDexClasses();
     for (DexType mainDexClass : mainDexClasses) {
-      if (!prunedClasses.contains(mainDexClass)) {
+      if (!prunedItems.getRemovedClasses().contains(mainDexClass)) {
         mainDexClassesAfterPruning.mainDexClasses.add(mainDexClass);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index dcdaafc..764fc9c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
 import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
 import com.google.common.collect.ImmutableList;
@@ -301,14 +302,13 @@
   // Commit of the synthetic items to a new fully populated application.
 
   public CommittedItems commit(DexApplication application) {
-    return commitPrunedClasses(application, Collections.emptySet());
+    return commitPrunedItems(PrunedItems.empty(application));
   }
 
-  public CommittedItems commitPrunedClasses(
-      DexApplication application, Set<DexType> removedClasses) {
+  public CommittedItems commitPrunedItems(PrunedItems prunedItems) {
     return commit(
-        application,
-        removedClasses,
+        prunedItems.getPrunedApp(),
+        prunedItems.getNoLongerSyntheticItems(),
         legacyPendingClasses,
         legacySyntheticTypes,
         pendingDefinitions,
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
index 6d50046..599fddd 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
@@ -120,6 +120,7 @@
     inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
   }
 
+  @Override
   public Set<V> values() {
     return inverse.keySet();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index 3ec2c15..1404c6a 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -25,4 +25,6 @@
   Map<K, V> getForwardMap();
 
   Set<K> keySet();
+
+  Set<V> values();
 }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
index 90217df..8e2edb5 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -6,7 +6,6 @@
 
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
@@ -144,7 +143,7 @@
   }
 
   @Override
-  public Collection<V> values() {
+  public Set<V> values() {
     return backing.values();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
index 69a28e6..f2c10c3 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
@@ -87,6 +87,11 @@
   }
 
   @Override
+  public Set<V> values() {
+    return Collections.emptySet();
+  }
+
+  @Override
   public BidirectionalOneToOneMap<V, K> getInverseOneToOneMap() {
     return new EmptyBidirectionalOneToOneMap<>();
   }