Replace all usages of GraphLens.mapProgramMethod() by a new LongLivedProgramMethodSetBuilder

Change-Id: Ia71277f3f2c42f8ad9d50076b7dc67d9e27aa108
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 9d06315..e5de7ff 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -163,19 +161,6 @@
     return newEncodedMethod;
   }
 
-  public ProgramMethod mapProgramMethod(
-      ProgramMethod oldMethod, DexDefinitionSupplier definitions) {
-    assert oldMethod.getDefinition() != DexEncodedMethod.SENTINEL;
-    assert oldMethod.getDefinition() != DexEncodedMethod.ANNOTATION_REFERENCE;
-    DexMethod newMethodReference = getRenamedMethodSignature(oldMethod.getReference());
-    DexProgramClass newHolder =
-        asProgramClassOrNull(definitions.definitionForHolder(newMethodReference));
-    assert newHolder != null;
-    DexEncodedMethod newMethod = newHolder.lookupMethod(newMethodReference);
-    assert newMethod != null;
-    return new ProgramMethod(newHolder, newMethod);
-  }
-
   public abstract DexType lookupType(DexType type);
 
   // This overload can be used when the graph lense is known to be context insensitive.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index ea88c95..2bfe820 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -901,7 +901,7 @@
       throws ExecutionException {
     assert !options.skipIR;
     ThreadUtils.processItems(
-        outliner.getMethodsSelectedForOutlining(),
+        outliner.buildMethodsSelectedForOutlining(),
         method -> {
           IRCode code = method.buildIR(appView);
           assert code != null;
@@ -1040,9 +1040,11 @@
 
   public void processMethodsConcurrently(ProgramMethodSet methods, ExecutorService executorService)
       throws ExecutionException {
-    OneTimeMethodProcessor processor = OneTimeMethodProcessor.getInstance(methods);
-    processor.forEachWave(
-        method -> processMethod(method, delayedOptimizationFeedback, processor), executorService);
+    if (!methods.isEmpty()) {
+      OneTimeMethodProcessor processor = OneTimeMethodProcessor.getInstance(methods);
+      processor.forEachWave(
+          method -> processMethod(method, delayedOptimizationFeedback, processor), executorService);
+    }
   }
 
   private String logCode(InternalOptions options, DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 562a463..7c136f6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.utils.IROrdering;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.ArrayDeque;
 import java.util.Collection;
@@ -57,7 +58,8 @@
   public static class Builder {
 
     private final Collection<CodeOptimization> defaultCodeOptimizations;
-    private final ProgramMethodSet methodsMap = ProgramMethodSet.create();
+    private final LongLivedProgramMethodSetBuilder methodsMap =
+        new LongLivedProgramMethodSetBuilder();
     private final Map<DexEncodedMethod, Collection<CodeOptimization>> optimizationsMap =
         new IdentityHashMap<>();
 
@@ -99,17 +101,12 @@
     // new signature. The compiler needs to update the set of methods that must be reprocessed
     // according to the graph lens.
     public void mapDexEncodedMethods(AppView<?> appView) {
-      ProgramMethodSet newMethodsMap = ProgramMethodSet.create();
       Map<DexEncodedMethod, Collection<CodeOptimization>> newOptimizationsMap =
           new IdentityHashMap<>();
-      methodsMap.forEach(
-          method -> newMethodsMap.add(appView.graphLense().mapProgramMethod(method, appView)));
       optimizationsMap.forEach(
           (method, optimizations) ->
               newOptimizationsMap.put(
                   appView.graphLense().mapDexEncodedMethod(method, appView), optimizations));
-      methodsMap.clear();
-      methodsMap.addAll(newMethodsMap);
       optimizationsMap.clear();
       optimizationsMap.putAll(newOptimizationsMap);
     }
@@ -128,7 +125,7 @@
                   if (definition != null) {
                     DexProgramClass clazz =
                         appView.definitionForHolder(definition).asProgramClass();
-                    set.add(new ProgramMethod(clazz, definition));
+                    set.createAndAdd(clazz, definition);
                   }
                 });
         put(set);
@@ -138,7 +135,8 @@
         return null;
       }
       CallGraph callGraph =
-          new PartialCallGraphBuilder(appView, methodsMap).build(executorService, timing);
+          new PartialCallGraphBuilder(appView, methodsMap.build(appView))
+              .build(executorService, timing);
       return new PostMethodProcessor(appView, optimizationsMap, callGraph);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 3210468..47ee95f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -355,7 +355,7 @@
   private void addSyntheticMethod(DexProgramClass clazz, DexEncodedMethod method) {
     newSyntheticMethods
         .computeIfAbsent(clazz, key -> ProgramMethodSet.create())
-        .add(new ProgramMethod(clazz, method));
+        .createAndAdd(clazz, method);
   }
 
   private void addICCEThrowingMethod(DexMethod method, DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 863e92d..a640fb6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -282,22 +281,21 @@
           assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
           continue;
         }
-        DexClass dexClass = appView.definitionFor(clazz.superType);
+        DexClass superclass = appView.definitionFor(clazz.superType);
         // Only performs computation if superclass is a library class, but not object to filter out
         // the most common case.
-        if (dexClass != null
-            && dexClass.isLibraryClass()
-            && dexClass.type != appView.dexItemFactory().objectType) {
-          for (DexType dexType : map.keySet()) {
-            if (inherit(dexClass.asLibraryClass(), dexType, emulatedDispatchMethods)) {
-              addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
-            }
-          }
+        if (superclass != null
+            && superclass.isLibraryClass()
+            && superclass.type != appView.dexItemFactory().objectType) {
+          map.forEach(
+              (type, methods) -> {
+                if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
+                  addInterfacesAndForwardingMethods(
+                      clazz, methods, method -> addedMethods.createAndAdd(clazz, method));
+                }
+              });
         }
       }
-      if (addedMethods.isEmpty()) {
-        return;
-      }
       converter.processMethodsConcurrently(addedMethods, executorService);
     }
 
@@ -320,14 +318,15 @@
       return false;
     }
 
-    private List<ProgramMethod> addInterfacesAndForwardingMethods(
-        DexProgramClass clazz, List<DexMethod> dexMethods) {
+    private void addInterfacesAndForwardingMethods(
+        DexProgramClass clazz,
+        List<DexMethod> methods,
+        Consumer<DexEncodedMethod> newForwardingMethodsConsumer) {
       // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
       // methods.
       // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
       // applies up to 24.
-      List<ProgramMethod> newForwardingMethods = new ArrayList<>();
-      for (DexMethod dexMethod : dexMethods) {
+      for (DexMethod dexMethod : methods) {
         DexType[] newInterfaces =
             Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1);
         newInterfaces[newInterfaces.length - 1] = dispatchInterfaceTypeFor(dexMethod);
@@ -336,10 +335,9 @@
         if (dexEncodedMethod == null) {
           DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz);
           clazz.addVirtualMethod(newMethod);
-          newForwardingMethods.add(new ProgramMethod(clazz, newMethod));
+          newForwardingMethodsConsumer.accept(newMethod);
         }
       }
-      return newForwardingMethods;
     }
 
     private DexEncodedMethod createForwardingMethod(DexMethod target, DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 1e93aa9..2b9456a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -68,6 +68,7 @@
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -94,14 +95,14 @@
  *   <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of
  *       methods found in the first step that are large enough (see {@link InternalOptions#outline}
  *       {@link OutlineOptions#threshold}), and the methods to be further analyzed for outlining is
- *       returned by {@link Outliner#getMethodsSelectedForOutlining}. Each selected method is then
+ *       returned by {@link Outliner#buildMethodsSelectedForOutlining}. Each selected method is then
  *       converted back to IR and passed to {@link Outliner#identifyOutlineSites(IRCode)}, which
  *       then stores concrete outlining candidates in {@link Outliner#outlineSites}.
  *   <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline
  *       support class</em> containing a static helper method for each outline candidate that occurs
  *       frequently enough. Each selected method is then converted to IR, passed to {@link
- *       Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and
- *       converted back to the output format (DEX or CF).
+ *       Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and converted back to
+ *       the output format (DEX or CF).
  * </ul>
  */
 public class Outliner {
@@ -109,7 +110,8 @@
   /** Result of first step (see {@link Outliner#createOutlineMethodIdentifierGenerator()}. */
   private final List<List<ProgramMethod>> candidateMethodLists = new ArrayList<>();
   /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
-  private final ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create();
+  private final LongLivedProgramMethodSetBuilder methodsSelectedForOutlining =
+      new LongLivedProgramMethodSetBuilder();
   /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
   private final Map<Outline, List<ProgramMethod>> outlineSites = new HashMap<>();
   /** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */
@@ -1311,19 +1313,15 @@
     assert outlineSites.isEmpty();
     for (List<ProgramMethod> outlineMethods : candidateMethodLists) {
       if (outlineMethods.size() >= appView.options().outline.threshold) {
-        for (ProgramMethod outlineMethod : outlineMethods) {
-          ProgramMethod mappedOutlineMethod =
-              appView.graphLense().mapProgramMethod(outlineMethod, appView);
-          methodsSelectedForOutlining.add(mappedOutlineMethod);
-        }
+        methodsSelectedForOutlining.addAll(outlineMethods);
       }
     }
     candidateMethodLists.clear();
     return !methodsSelectedForOutlining.isEmpty();
   }
 
-  public ProgramMethodSet getMethodsSelectedForOutlining() {
-    return methodsSelectedForOutlining;
+  public ProgramMethodSet buildMethodsSelectedForOutlining() {
+    return methodsSelectedForOutlining.build(appView);
   }
 
   public DexProgramClass buildOutlinerClass(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 0141c6e..06c2894 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -314,7 +314,6 @@
           appView
               .appInfo()
               .rewrittenWithLens(appView.appInfo().app().asDirect(), enumUnboxingLens));
-      classStaticizer.filterCandidates();
       // Update optimization info.
       feedback.fixupOptimizationInfos(
           appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 01f1800..2a5e9cd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -52,6 +52,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -194,7 +195,8 @@
   // we mark a method for further processing, and then invalidate the only lambda referenced
   // from it. In this case we will reprocess method that does not need patching, but it
   // should not be happening very frequently and we ignore possible overhead.
-  private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.create();
+  private final LongLivedProgramMethodSetBuilder methodsToReprocess =
+      new LongLivedProgramMethodSetBuilder();
 
   private final AppView<AppInfoWithLiveness> appView;
   private final Kotlin kotlin;
@@ -453,11 +455,7 @@
     if (methodsToReprocess.isEmpty()) {
       return;
     }
-    ProgramMethodSet methods = ProgramMethodSet.create();
-    methodsToReprocess.forEach(
-        method -> {
-          methods.add(appView.graphLense().mapProgramMethod(method, appView));
-        });
+    ProgramMethodSet methods = methodsToReprocess.build(appView);
     converter.processMethodsConcurrently(methods, executorService);
     assert methods.stream()
         .map(DexClassAndMethod::getDefinition)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 21ed08c..675e8f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -34,7 +34,8 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
+import com.android.tools.r8.utils.collections.ProgramMethodSetOrBuilder;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -68,7 +69,7 @@
     final AtomicInteger fieldWrites = new AtomicInteger();
     // Number of instances created.
     final AtomicInteger instancesCreated = new AtomicInteger();
-    final ProgramMethodSet referencedFrom = ProgramMethodSet.create();
+    ProgramMethodSetOrBuilder referencedFrom = new LongLivedProgramMethodSetBuilder();
     final AtomicReference<DexEncodedMethod> constructor = new AtomicReference<>();
     final AtomicReference<DexEncodedMethod> getter = new AtomicReference<>();
 
@@ -100,16 +101,6 @@
       candidates.remove(candidate.type);
       return null;
     }
-
-    void filterMethods() {
-      ProgramMethodSet newReferencedFrom = ProgramMethodSet.create();
-      for (ProgramMethod method : referencedFrom) {
-        ProgramMethod mapped = appView.graphLense().mapProgramMethod(method, appView);
-        newReferencedFrom.add(mapped);
-      }
-      referencedFrom.clear();
-      referencedFrom.addAll(newReferencedFrom);
-    }
   }
 
   // The map storing all the potential candidates for staticizing.
@@ -298,7 +289,7 @@
             }
             // Ignore just read instruction.
           }
-          candidateInfo.referencedFrom.add(method);
+          candidateInfo.referencedFrom.asLongLivedBuilder().add(method);
         }
         continue;
       }
@@ -318,7 +309,7 @@
         // Check the field being read: make sure all usages are valid.
         CandidateInfo info = processStaticFieldRead(instruction.asStaticGet());
         if (info != null) {
-          info.referencedFrom.add(method);
+          info.referencedFrom.asLongLivedBuilder().add(method);
           // If the candidate is still valid, ignore all usages in further analysis.
           Value value = instruction.outValue();
           if (value != null) {
@@ -332,7 +323,7 @@
         // Check if it is a static singleton getter.
         CandidateInfo info = processInvokeStatic(instruction.asInvokeStatic());
         if (info != null) {
-          info.referencedFrom.add(method);
+          info.referencedFrom.asLongLivedBuilder().add(method);
           // If the candidate is still valid, ignore all usages in further analysis.
           Value value = instruction.outValue();
           if (value != null) {
@@ -668,17 +659,6 @@
     return false;
   }
 
-  // Methods may have their signature changed in-between the IR processing rounds, leading to
-  // duplicates where one version is the outdated version. Remove these.
-  // This also ensures no unboxed enum are staticized, if that would be the case, then
-  // the candidate would need to be removed from the candidate list.
-  public void filterCandidates() {
-    for (Map.Entry<DexType, CandidateInfo> entry : candidates.entrySet()) {
-      assert !appView.unboxedEnums().containsEnum(entry.getKey());
-      entry.getValue().filterMethods();
-    }
-  }
-
   // Perform staticizing candidates:
   //
   //  1. After filtering candidates based on usage, finalize the list of candidates by
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 4d21056..efcf89f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -219,9 +219,12 @@
         continue;
       }
 
+      ProgramMethodSet referencedFrom = info.referencedFrom.asLongLivedBuilder().build(appView);
+      info.referencedFrom = referencedFrom;
+
       // CHECK: references to field read usages are fixable.
       boolean fixableFieldReads = true;
-      for (ProgramMethod method : info.referencedFrom) {
+      for (ProgramMethod method : referencedFrom) {
         IRCode code = method.buildIR(appView);
         assert code != null;
         List<Instruction> singletonUsers =
@@ -298,8 +301,9 @@
       if (getter != null) {
         singletonGetters.put(getter.method, candidate);
       }
-      assert validMethods(candidate.referencedFrom);
-      referencingExtraMethods.addAll(candidate.referencedFrom);
+      ProgramMethodSet referencedFrom = candidate.referencedFrom.asSet();
+      assert validMethods(referencedFrom);
+      referencingExtraMethods.addAll(referencedFrom);
     }
 
     removedInstanceMethods.forEach(referencingExtraMethods::remove);
@@ -720,7 +724,7 @@
         } else if (!factory().isConstructor(method.method)) {
           DexEncodedMethod staticizedMethod = method.toStaticMethodWithoutThis();
           newDirectMethods.add(staticizedMethod);
-          staticizedMethods.add(new ProgramMethod(candidateClass, staticizedMethod));
+          staticizedMethods.createAndAdd(candidateClass, staticizedMethod);
           methodMapping.put(method.method, staticizedMethod.method);
         }
       }
@@ -806,7 +810,7 @@
         if (staticizedMethods.remove(method)) {
           // Properly update staticized methods to reprocess, i.e., add the corresponding one that
           // has just been migrated to the host class.
-          staticizedMethods.add(new ProgramMethod(hostClass, newMethod));
+          staticizedMethods.createAndAdd(hostClass, newMethod);
         }
         DexMethod originalMethod = methodMapping.inverse().get(method.method);
         if (originalMethod == null) {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
new file mode 100644
index 0000000..269b24d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -0,0 +1,52 @@
+// 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.utils.collections;
+
+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.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+public class LongLivedProgramMethodSetBuilder implements ProgramMethodSetOrBuilder {
+
+  private Set<DexMethod> methods = Sets.newIdentityHashSet();
+
+  public LongLivedProgramMethodSetBuilder() {}
+
+  public void add(ProgramMethod method) {
+    methods.add(method.getReference());
+  }
+
+  public void addAll(Iterable<ProgramMethod> methods) {
+    methods.forEach(this::add);
+  }
+
+  public ProgramMethodSet build(AppView<AppInfoWithLiveness> appView) {
+    ProgramMethodSet result = ProgramMethodSet.create(methods.size());
+    for (DexMethod oldMethod : methods) {
+      DexMethod method = appView.graphLense().getRenamedMethodSignature(oldMethod);
+      DexProgramClass holder = appView.definitionForHolder(method).asProgramClass();
+      result.createAndAdd(holder, holder.lookupMethod(method));
+    }
+    return result;
+  }
+
+  public boolean isEmpty() {
+    return methods.isEmpty();
+  }
+
+  @Override
+  public boolean isLongLivedBuilder() {
+    return true;
+  }
+
+  @Override
+  public LongLivedProgramMethodSetBuilder asLongLivedBuilder() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index 0ca3738..d288177 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
@@ -17,7 +18,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Stream;
 
-public class ProgramMethodSet implements Iterable<ProgramMethod> {
+public class ProgramMethodSet implements ProgramMethodSetOrBuilder, Iterable<ProgramMethod> {
 
   private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap.of());
 
@@ -67,6 +68,15 @@
     backing.putAll(methods.backing);
   }
 
+  @Override
+  public ProgramMethodSet asSet() {
+    return this;
+  }
+
+  public boolean createAndAdd(DexProgramClass clazz, DexEncodedMethod definition) {
+    return add(new ProgramMethod(clazz, definition));
+  }
+
   public boolean contains(DexEncodedMethod method) {
     return backing.containsKey(method.getReference());
   }
@@ -83,6 +93,11 @@
     return backing.isEmpty();
   }
 
+  @Override
+  public Iterator<ProgramMethod> iterator() {
+    return backing.values().iterator();
+  }
+
   public boolean remove(DexMethod method) {
     ProgramMethod existing = backing.remove(method);
     return existing != null;
@@ -106,9 +121,4 @@
     forEach(method -> definitions.add(method.getDefinition()));
     return definitions;
   }
-
-  @Override
-  public Iterator<ProgramMethod> iterator() {
-    return backing.values().iterator();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSetOrBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSetOrBuilder.java
new file mode 100644
index 0000000..f7298be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSetOrBuilder.java
@@ -0,0 +1,20 @@
+// 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.utils.collections;
+
+public interface ProgramMethodSetOrBuilder {
+
+  default boolean isLongLivedBuilder() {
+    return false;
+  }
+
+  default LongLivedProgramMethodSetBuilder asLongLivedBuilder() {
+    return null;
+  }
+
+  default ProgramMethodSet asSet() {
+    return null;
+  }
+}