Rewrite and prune misc. information on AppInfoWithLiveness

Change-Id: I0d14841db5cd3f37d5f27386036aa75c430d3acd
diff --git a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
index a30eb47..b8e779d 100644
--- a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
+++ b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
@@ -86,6 +86,8 @@
   abstract AbstractAccessContexts rewrittenWithLens(
       DexDefinitionSupplier definitions, GraphLens lens);
 
+  abstract AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems);
+
   public static EmptyAccessContexts empty() {
     return EmptyAccessContexts.getInstance();
   }
@@ -155,6 +157,11 @@
     public AbstractAccessContexts join(AbstractAccessContexts contexts) {
       return contexts;
     }
+
+    @Override
+    AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+      return this;
+    }
   }
 
   public static class ConcreteAccessContexts extends AbstractAccessContexts {
@@ -378,6 +385,14 @@
       contexts.asConcrete().accessesWithContexts.forEach(addAllMethods);
       return new ConcreteAccessContexts(newAccessesWithContexts);
     }
+
+    @Override
+    AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+      for (ProgramMethodSet methodSet : accessesWithContexts.values()) {
+        methodSet.removeIf(method -> prunedItems.isRemoved(method.getReference()));
+      }
+      return this;
+    }
   }
 
   public static class UnknownAccessContexts extends AbstractAccessContexts {
@@ -440,5 +455,10 @@
     public AbstractAccessContexts join(AbstractAccessContexts contexts) {
       return this;
     }
+
+    @Override
+    AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+      return this;
+    }
   }
 }
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 9ceb56c..c09b0fd 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -880,7 +880,6 @@
       testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
     } else {
       assert this.verticallyMergedClasses != null;
-      assert verticallyMergedClasses.isEmpty();
     }
   }
 
@@ -1097,7 +1096,10 @@
                 public void run(Timing timing) {
                   if (appView.hasLiveness()) {
                     result =
-                        appView.appInfoWithLiveness().rewrittenWithLens(application, lens, timing);
+                        appView
+                            .appInfoWithLiveness()
+                            .rewrittenWithLens(
+                                application, lens, appliedLensInModifiedLens, timing);
                   } else {
                     assert appView.hasClassHierarchy();
                     AppView<AppInfoWithClassHierarchy> appViewWithClassHierarchy =
@@ -1255,6 +1257,23 @@
                 public boolean shouldRun() {
                   return !appView.getStartupProfile().isEmpty();
                 }
+              },
+              new ThreadTask() {
+                @Override
+                public void run(Timing timing) {
+                  ImmutableSet.Builder<DexMethod> cfByteCodePassThroughBuilder =
+                      ImmutableSet.builder();
+                  for (DexMethod method : appView.cfByteCodePassThrough) {
+                    cfByteCodePassThroughBuilder.add(
+                        lens.getRenamedMethodSignature(method, appliedLensInModifiedLens));
+                  }
+                  appView.cfByteCodePassThrough = cfByteCodePassThroughBuilder.build();
+                }
+
+                @Override
+                public boolean shouldRun() {
+                  return !appView.cfByteCodePassThrough.isEmpty();
+                }
               });
         });
 
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 6b2bad4..29cd5a4 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -9,7 +9,9 @@
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -102,4 +104,17 @@
     assert infos.values().size() == SetUtils.newIdentityHashSet(infos.values()).size();
     return true;
   }
+
+  public FieldAccessInfoCollectionImpl withoutPrunedItems(PrunedItems prunedItems) {
+    Iterator<Entry<DexField, FieldAccessInfoImpl>> iterator = infos.entrySet().iterator();
+    while (iterator.hasNext()) {
+      Entry<DexField, FieldAccessInfoImpl> entry = iterator.next();
+      if (prunedItems.isRemoved(entry.getKey())) {
+        iterator.remove();
+      } else {
+        entry.setValue(entry.getValue().withoutPrunedItems(prunedItems));
+      }
+    }
+    return this;
+  }
 }
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 0dae257..39aa866 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -406,4 +406,10 @@
     merged.writesWithContexts = writesWithContexts.join(impl.writesWithContexts);
     return merged;
   }
+
+  public FieldAccessInfoImpl withoutPrunedItems(PrunedItems prunedItems) {
+    readsWithContexts = readsWithContexts.withoutPrunedItems(prunedItems);
+    writesWithContexts = writesWithContexts.withoutPrunedItems(prunedItems);
+    return this;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
index 3b8681b..b541bfc 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -17,7 +17,9 @@
 import com.android.tools.r8.utils.collections.ThrowingMap;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiConsumer;
@@ -178,18 +180,18 @@
     }
   }
 
-  public MethodAccessInfoCollection withoutPrunedItems(PrunedItems prunedItems) {
+  public MethodAccessInfoCollection withoutPrunedContexts(PrunedItems prunedItems) {
     if (!fullyDestroyed) {
-      pruneItems(prunedItems, directInvokes);
-      pruneItems(prunedItems, interfaceInvokes);
-      pruneItems(prunedItems, staticInvokes);
-      pruneItems(prunedItems, superInvokes);
-      pruneItems(prunedItems, virtualInvokes);
+      pruneContexts(prunedItems, directInvokes);
+      pruneContexts(prunedItems, interfaceInvokes);
+      pruneContexts(prunedItems, staticInvokes);
+      pruneContexts(prunedItems, superInvokes);
+      pruneContexts(prunedItems, virtualInvokes);
     }
     return this;
   }
 
-  private static void pruneItems(
+  private static void pruneContexts(
       PrunedItems prunedItems, Map<DexMethod, ProgramMethodSet> invokes) {
     if (isThrowingMap(invokes)) {
       return;
@@ -212,6 +214,45 @@
             });
   }
 
+  public MethodAccessInfoCollection withoutPrunedItems(PrunedItems prunedItems) {
+    if (!fullyDestroyed) {
+      pruneItems(prunedItems, directInvokes);
+      pruneItems(prunedItems, interfaceInvokes);
+      pruneItems(prunedItems, staticInvokes);
+      pruneItems(prunedItems, superInvokes);
+      pruneItems(prunedItems, virtualInvokes);
+    }
+    return this;
+  }
+
+  private static void pruneItems(
+      PrunedItems prunedItems, Map<DexMethod, ProgramMethodSet> invokes) {
+    if (isThrowingMap(invokes)) {
+      return;
+    }
+    Iterator<Entry<DexMethod, ProgramMethodSet>> iterator = invokes.entrySet().iterator();
+    while (iterator.hasNext()) {
+      Entry<DexMethod, ProgramMethodSet> entry = iterator.next();
+      if (prunedItems.isRemoved(entry.getKey())) {
+        iterator.remove();
+      } else {
+        ProgramMethodSet contexts = entry.getValue();
+        contexts.removeIf(
+            context -> {
+              if (prunedItems.isRemoved(context.getReference())) {
+                return true;
+              }
+              assert prunedItems.getPrunedApp().definitionFor(context.getReference()) != null
+                  : "Expected method to be present: " + context.getReference().toSourceString();
+              return false;
+            });
+        if (contexts.isEmpty()) {
+          iterator.remove();
+        }
+      }
+    }
+  }
+
   public boolean verify(AppView<AppInfoWithLiveness> appView) {
     assert verifyNoNonResolving(appView);
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
index 70d1666..b55811b 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -43,5 +43,5 @@
       AppInfo appInfo);
 
   ObjectAllocationInfoCollection rewrittenWithLens(
-      DexDefinitionSupplier definitions, GraphLens lens, Timing timing);
+      DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens, Timing timing);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 6050c9d..2ffe096 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
 import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.shaking.GraphReporter;
 import com.android.tools.r8.shaking.InstantiationReason;
@@ -144,14 +145,17 @@
 
   @Override
   public ObjectAllocationInfoCollectionImpl rewrittenWithLens(
-      DexDefinitionSupplier definitions, GraphLens lens, Timing timing) {
+      DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens, Timing timing) {
     return timing.time(
-        "Rewrite ObjectAllocationInfoCollectionImpl", () -> rewrittenWithLens(definitions, lens));
+        "Rewrite ObjectAllocationInfoCollectionImpl",
+        () -> rewrittenWithLens(definitions, lens, appliedLens));
   }
 
   private ObjectAllocationInfoCollectionImpl rewrittenWithLens(
-      DexDefinitionSupplier definitions, GraphLens lens) {
-    return builder(true, null).rewrittenWithLens(this, definitions, lens).build(definitions);
+      DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens) {
+    return builder(true, null)
+        .rewrittenWithLens(this, definitions, lens, appliedLens)
+        .build(definitions);
   }
 
   public ObjectAllocationInfoCollectionImpl withoutPrunedItems(PrunedItems prunedItems) {
@@ -474,11 +478,12 @@
     Builder rewrittenWithLens(
         ObjectAllocationInfoCollectionImpl objectAllocationInfos,
         DexDefinitionSupplier definitions,
-        GraphLens lens) {
+        GraphLens lens,
+        GraphLens appliedLens) {
       instantiatedHierarchy = null;
       objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach(
           clazz -> {
-            DexType type = lens.lookupType(clazz.type);
+            DexType type = lens.lookupType(clazz.type, appliedLens);
             if (type.isPrimitiveType()) {
               return;
             }
@@ -488,7 +493,7 @@
           });
       objectAllocationInfos.classesWithAllocationSiteTracking.forEach(
           (clazz, allocationSitesForClass) -> {
-            DexType type = lens.lookupType(clazz.type);
+            DexType type = lens.lookupType(clazz.type, appliedLens);
             if (type.isPrimitiveType()) {
               return;
             }
@@ -507,7 +512,7 @@
           });
       for (DexProgramClass abstractType :
           objectAllocationInfos.interfacesWithUnknownSubtypeHierarchy) {
-        DexType type = lens.lookupType(abstractType.type);
+        DexType type = lens.lookupType(abstractType.type, appliedLens);
         if (type.isPrimitiveType()) {
           assert false;
           continue;
@@ -517,15 +522,19 @@
         assert !interfacesWithUnknownSubtypeHierarchy.contains(rewrittenClass);
         interfacesWithUnknownSubtypeHierarchy.add(rewrittenClass);
       }
+      LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(definitions, lens, appliedLens);
       objectAllocationInfos.instantiatedLambdas.forEach(
           (iface, lambdas) -> {
-            DexType type = lens.lookupType(iface);
+            DexType type = lens.lookupType(iface, appliedLens);
             if (type.isPrimitiveType()) {
               assert false;
               return;
             }
-            // TODO(b/150277553): Rewrite lambda descriptor.
-            instantiatedLambdas.computeIfAbsent(type, ignoreKey(ArrayList::new)).addAll(lambdas);
+            List<LambdaDescriptor> newLambdas =
+                instantiatedLambdas.computeIfAbsent(type, ignoreKey(ArrayList::new));
+            for (LambdaDescriptor lambda : lambdas) {
+              newLambdas.add(lambda.rewrittenWithLens(lens, appliedLens, rewriter));
+            }
           });
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 013bfde..0e31e93 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -118,15 +118,19 @@
         .getName();
   }
 
-  @SuppressWarnings("ReferenceEquality")
   public DexMethodHandle rewriteDexMethodHandle(
       DexMethodHandle methodHandle, MethodHandleUse use, ProgramMethod context) {
+    return rewriteDexMethodHandle(methodHandle, use, context.getReference());
+  }
+
+  @SuppressWarnings("ReferenceEquality")
+  public DexMethodHandle rewriteDexMethodHandle(
+      DexMethodHandle methodHandle, MethodHandleUse use, DexMethod context) {
     if (methodHandle.isMethodHandle()) {
       DexMethod invokedMethod = methodHandle.asMethod();
       MethodHandleType oldType = methodHandle.type;
       MethodLookupResult lensLookup =
-          graphLens.lookupMethod(
-              invokedMethod, context.getReference(), oldType.toInvokeType(), codeLens);
+          graphLens.lookupMethod(invokedMethod, context, oldType.toInvokeType(), codeLens);
       DexMethod rewrittenTarget = lensLookup.getReference();
       DexMethod actualTarget;
       MethodHandleType newType;
@@ -161,7 +165,7 @@
         }
       }
       if (newType != oldType || actualTarget != invokedMethod || rewrittenTarget != actualTarget) {
-        DexClass holder = definitions.definitionFor(actualTarget.holder, context);
+        DexClass holder = definitions.definitionFor(actualTarget.holder);
         boolean isInterface = holder != null ? holder.isInterface() : methodHandle.isInterface;
         return definitions
             .dexItemFactory()
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 1c9d26d..e3cbc07 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
@@ -20,6 +20,10 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.SetUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
@@ -42,8 +46,8 @@
   final DexProto enforcedProto;
   public final DexMethodHandle implHandle;
 
-  public final List<DexType> interfaces = new ArrayList<>();
-  public final Set<DexProto> bridges = Sets.newIdentityHashSet();
+  public final List<DexType> interfaces;
+  public final Set<DexProto> bridges;
   public final DexTypeList captures;
 
   // Used for accessibility analysis and few assertions only.
@@ -51,13 +55,7 @@
   private final DexType targetHolder;
 
   private LambdaDescriptor() {
-    uniqueId = null;
-    enforcedProto = null;
-    implHandle = null;
-    captures = null;
-    targetAccessFlags = null;
-    targetHolder = null;
-    mainMethod = null;
+    this(null, null, null, null, null, null, null, null, null);
   }
 
   public DexProto getErasedProto() {
@@ -96,7 +94,8 @@
     this.enforcedProto = enforcedProto;
     this.implHandle = implHandle;
     this.captures = captures;
-
+    this.bridges = Sets.newIdentityHashSet();
+    this.interfaces = new ArrayList<>();
     this.interfaces.add(mainInterface);
     DexClassAndMethod targetMethod =
         context == null ? null : lookupTargetMethod(appView, appInfo, context);
@@ -109,6 +108,27 @@
     }
   }
 
+  private LambdaDescriptor(
+      String uniqueId,
+      DexMethod mainMethod,
+      DexProto enforcedProto,
+      DexMethodHandle implHandle,
+      List<DexType> interfaces,
+      Set<DexProto> bridges,
+      DexTypeList captures,
+      MethodAccessFlags targetAccessFlags,
+      DexType targetHolder) {
+    this.uniqueId = uniqueId;
+    this.mainMethod = mainMethod;
+    this.enforcedProto = enforcedProto;
+    this.implHandle = implHandle;
+    this.interfaces = interfaces;
+    this.bridges = bridges;
+    this.captures = captures;
+    this.targetAccessFlags = targetAccessFlags;
+    this.targetHolder = targetHolder;
+  }
+
   final DexType getImplReceiverType() {
     // The receiver of instance impl-method is captured as the first captured
     // value or should be the first argument of the enforced method signature.
@@ -498,4 +518,32 @@
 
     return false;
   }
+
+  public LambdaDescriptor rewrittenWithLens(
+      GraphLens lens, GraphLens appliedLens, LensCodeRewriterUtils rewriter) {
+    String newUniqueId = uniqueId;
+    DexMethod newMainMethod = lens.getRenamedMethodSignature(mainMethod, appliedLens);
+    DexProto newEnforcedProto = rewriter.rewriteProto(enforcedProto);
+    DexMethodHandle newImplHandle =
+        rewriter.rewriteDexMethodHandle(
+            implHandle, MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY, mainMethod);
+    List<DexType> newInterfaces =
+        new ArrayList<>(
+            SetUtils.mapLinkedHashSet(interfaces, itf -> lens.lookupType(itf, appliedLens)));
+    Set<DexProto> newBridges = SetUtils.mapIdentityHashSet(bridges, rewriter::rewriteProto);
+    DexTypeList newCaptures = captures.map(capture -> lens.lookupType(capture, appliedLens));
+    MethodAccessFlags newTargetAccessFlags = targetAccessFlags;
+    DexType newTargetHolder =
+        targetHolder != null ? lens.lookupType(targetHolder, appliedLens) : null;
+    return new LambdaDescriptor(
+        newUniqueId,
+        newMainMethod,
+        newEnforcedProto,
+        newImplHandle,
+        newInterfaces,
+        newBridges,
+        newCaptures,
+        newTargetAccessFlags,
+        newTargetHolder);
+  }
 }
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 93ac4f4..e634f97 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Definition;
+import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndField;
@@ -316,8 +317,8 @@
         pruneMethods(previous.bootstrapMethods, prunedItems, tasks),
         pruneMethods(previous.virtualMethodsTargetedByInvokeDirect, prunedItems, tasks),
         pruneMethods(previous.liveMethods, prunedItems, tasks),
-        previous.fieldAccessInfoCollection,
-        previous.methodAccessInfoCollection.withoutPrunedItems(prunedItems),
+        previous.fieldAccessInfoCollection.withoutPrunedItems(prunedItems),
+        previous.methodAccessInfoCollection.withoutPrunedContexts(prunedItems),
         previous.objectAllocationInfoCollection.withoutPrunedItems(prunedItems),
         pruneCallSites(previous.callSites, prunedItems),
         extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
@@ -1090,12 +1091,19 @@
     return appInfoWithLiveness;
   }
 
+  public AppInfoWithLiveness rebuildWithLiveness(DexApplication application) {
+    return rebuildWithLiveness(getSyntheticItems().commit(application));
+  }
+
   public AppInfoWithLiveness rebuildWithLiveness(CommittedItems committedItems) {
     return new AppInfoWithLiveness(this, committedItems);
   }
 
   public AppInfoWithLiveness rewrittenWithLens(
-      DirectMappedDexApplication application, NonIdentityGraphLens lens, Timing timing) {
+      DirectMappedDexApplication application,
+      NonIdentityGraphLens lens,
+      GraphLens appliedLens,
+      Timing timing) {
     assert checkIfObsolete();
 
     // Switchmap classes should never be affected by renaming.
@@ -1126,7 +1134,8 @@
         lens.rewriteReferences(liveMethods),
         fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
         methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
-        objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
+        objectAllocationInfoCollection.rewrittenWithLens(
+            definitionSupplier, lens, appliedLens, timing),
         lens.rewriteCallSites(callSites, definitionSupplier, timing),
         keepInfo.rewrite(definitionSupplier, lens, application.options, timing),
         // Take any rule in case of collisions.
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index be05c5d..a7c3366 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -6,9 +6,11 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
@@ -115,7 +117,7 @@
     return builder.build();
   }
 
-  public static <T, S> Set<T> mapIdentityHashSet(Set<S> set, Function<S, T> fn) {
+  public static <T, S> Set<T> mapIdentityHashSet(Collection<S> set, Function<S, T> fn) {
     Set<T> out = newIdentityHashSet(set.size());
     for (S element : set) {
       out.add(fn.apply(element));
@@ -123,6 +125,14 @@
     return out;
   }
 
+  public static <T, S> Set<T> mapLinkedHashSet(Collection<S> set, Function<S, T> fn) {
+    Set<T> out = new LinkedHashSet<>(set.size());
+    for (S element : set) {
+      out.add(fn.apply(element));
+    }
+    return out;
+  }
+
   public static <T> T removeFirst(Set<T> set) {
     T element = set.iterator().next();
     set.remove(element);