Towards using DexClassAndMethod in desugaring

Change-Id: I49207ae20080114590ab90fedba49418b699a948
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 70bad80..22a830e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import static com.google.common.base.Predicates.alwaysFalse;
+import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
@@ -121,6 +122,16 @@
     }
   }
 
+  public void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer) {
+    forEachClassMethodMatching(alwaysTrue(), consumer);
+  }
+
+  public void forEachClassMethodMatching(
+      Predicate<DexEncodedMethod> predicate, Consumer<? super DexClassAndMethod> consumer) {
+    methodCollection.forEachMethodMatching(
+        predicate, method -> consumer.accept(DexClassAndMethod.create(this, method)));
+  }
+
   @Override
   public ClassAccessFlags getAccessFlags() {
     return accessFlags;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 0a17134..0446e09 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -27,11 +27,23 @@
     }
   }
 
+  public boolean isDefaultMethod() {
+    return getHolder().isInterface() && getDefinition().isDefaultMethod();
+  }
+
+  public boolean isStructurallyEqualTo(DexClassAndMethod other) {
+    return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
+  }
+
   @Override
   public MethodAccessFlags getAccessFlags() {
     return getDefinition().getAccessFlags();
   }
 
+  public DexProto getProto() {
+    return getReference().getProto();
+  }
+
   @Override
   public boolean isMethodTarget() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 4fcd92b..91203c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1383,13 +1383,13 @@
   }
 
   public static DexEncodedMethod createDesugaringForwardingMethod(
-      DexEncodedMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
-    DexMethod method = target.method;
+      DexClassAndMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
+    DexMethod method = target.getReference();
     assert forwardMethod != null;
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
     DexMethod newMethod = factory.createMethod(clazz.type, method.proto, method.name);
-    MethodAccessFlags newFlags = target.accessFlags.copy();
+    MethodAccessFlags newFlags = target.getAccessFlags().copy();
     // Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
     newFlags.setSynthetic();
     newFlags.unsetAbstract();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 33063ba..3dc807c 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -52,10 +52,6 @@
     definition.parameterAnnotationsList.collectIndexedItems(indexedItems);
   }
 
-  public boolean isStructurallyEqualTo(ProgramMethod other) {
-    return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
-  }
-
   public void registerCodeReferences(UseRegistry registry) {
     Code code = getDefinition().getCode();
     if (code != null) {
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 1e3b81f..6200603 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -25,6 +27,7 @@
 import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -39,7 +42,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import org.objectweb.asm.Opcodes;
 
@@ -105,14 +107,14 @@
     // List of methods that are known to be forwarded to by a forwarding method at this point in the
     // class hierarchy. This set consists of the default interface methods, i.e., the targets of the
     // forwarding methods, *not* the forwarding methods themselves.
-    final ImmutableList<DexEncodedMethod> forwardedMethodTargets;
+    final ImmutableList<DexClassAndMethod> forwardedMethodTargets;
     // If the forwarding methods for the emulated interface methods have not been added yet,
     // this contains the information to add it in the subclasses.
     final EmulatedInterfaceInfo emulatedInterfaceInfo;
 
     ClassInfo(
         ClassInfo parent,
-        ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+        ImmutableList<DexClassAndMethod> forwardedMethodTargets,
         EmulatedInterfaceInfo emulatedInterfaceInfo) {
       this.parent = parent;
       this.forwardedMethodTargets = forwardedMethodTargets;
@@ -121,7 +123,7 @@
 
     static ClassInfo create(
         ClassInfo parent,
-        ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+        ImmutableList<DexClassAndMethod> forwardedMethodTargets,
         EmulatedInterfaceInfo emulatedInterfaceInfo) {
       return forwardedMethodTargets.isEmpty()
           ? parent
@@ -132,8 +134,11 @@
       return this == EMPTY;
     }
 
-    boolean isTargetedByForwards(DexEncodedMethod method) {
-      return forwardedMethodTargets.contains(method)
+    boolean isTargetedByForwards(DexClassAndMethod method) {
+      return IterableUtils.any(
+              forwardedMethodTargets,
+              DexClassAndMember::getDefinition,
+              definition -> definition == method.getDefinition())
           || (parent != null && parent.isTargetedByForwards(method));
     }
   }
@@ -394,7 +399,7 @@
   // The computation of a class information and the insertions of forwarding methods.
   private ClassInfo computeClassInfo(
       DexClass clazz, ClassInfo superInfo, SignaturesInfo signatureInfo) {
-    Builder<DexEncodedMethod> additionalForwards = ImmutableList.builder();
+    ImmutableList.Builder<DexClassAndMethod> additionalForwards = ImmutableList.builder();
     // First we deal with non-emulated interface desugaring.
     resolveForwardingMethods(clazz, superInfo, signatureInfo.signatures, additionalForwards);
     // Second we deal with emulated interface, if one method has override in the current class,
@@ -507,7 +512,7 @@
       DexClass clazz,
       ClassInfo superInfo,
       MethodSignatures signatures,
-      Builder<DexEncodedMethod> additionalForwards) {
+      Builder<DexClassAndMethod> additionalForwards) {
     if (clazz.isProgramClass() && appView.isAlreadyLibraryDesugared(clazz.asProgramClass())) {
       return;
     }
@@ -515,10 +520,10 @@
       resolveForwardForSignature(
           clazz,
           wrapper.get(),
-          (targetHolder, target) -> {
+          target -> {
             if (!superInfo.isTargetedByForwards(target)) {
               additionalForwards.add(target);
-              addForwardingMethod(targetHolder, target, clazz);
+              addForwardingMethod(target, clazz);
             }
           });
     }
@@ -527,7 +532,7 @@
   // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
-      DexClass clazz, DexMethod method, BiConsumer<DexClass, DexEncodedMethod> addForward) {
+      DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
     // Resolve the default method with base type as the symbolic holder as call sites are not known.
     // The dispatch target is then looked up from the possible "instance" class.
     // Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype
@@ -550,54 +555,52 @@
       return;
     }
 
-    DexEncodedMethod target = virtualDispatchTarget.getDefinition();
-    DexClass targetHolder = virtualDispatchTarget.getHolder();
     // Don't forward if the target is explicitly marked as 'dont-rewrite'
-    if (dontRewrite(targetHolder, target)) {
+    if (dontRewrite(virtualDispatchTarget)) {
       return;
     }
 
     // If resolution targets a default interface method, forward it.
-    if (targetHolder.isInterface() && target.isDefaultMethod()) {
-      addForward.accept(targetHolder, target);
+    if (virtualDispatchTarget.isDefaultMethod()) {
+      addForward.accept(virtualDispatchTarget);
       return;
     }
 
     // Remaining edge cases only pertain to desugaring of library methods.
-    DexLibraryClass libraryHolder = targetHolder.asLibraryClass();
-    if (libraryHolder == null || ignoreLibraryInfo()) {
+    if (!virtualDispatchTarget.isLibraryMethod() || ignoreLibraryInfo()) {
       return;
     }
 
-    if (isRetargetMethod(libraryHolder, target)) {
-      addForward.accept(targetHolder, target);
+    LibraryMethod libraryMethod = virtualDispatchTarget.asLibraryMethod();
+    if (isRetargetMethod(libraryMethod)) {
+      addForward.accept(virtualDispatchTarget);
       return;
     }
 
     // If target is a non-interface library class it may be an emulated interface,
     // except on a rewritten type, where L8 has already dealt with the desugaring.
-    if (!libraryHolder.isInterface()
-        && !appView.rewritePrefix.hasRewrittenType(libraryHolder.type, appView)) {
+    if (!libraryMethod.getHolder().isInterface()
+        && !appView.rewritePrefix.hasRewrittenType(libraryMethod.getHolderType(), appView)) {
       // Here we use step-3 of resolution to find a maximally specific default interface method.
-      DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(libraryHolder, method);
-      if (result != null && rewriter.isEmulatedInterface(result.getHolder().type)) {
-        addForward.accept(result.getHolder(), result.getDefinition());
+      DexClassAndMethod result =
+          appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
+      if (result != null && rewriter.isEmulatedInterface(result.getHolderType())) {
+        addForward.accept(result);
       }
     }
   }
 
-  private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) {
+  private boolean isRetargetMethod(LibraryMethod method) {
     assert needsLibraryInfo();
-    assert holder.type == method.getHolderType();
-    assert method.isNonPrivateVirtualMethod();
-    if (method.isFinal()) {
-      return false;
-    }
-    return appView.options().desugaredLibraryConfiguration.retargetMethod(method, appView) != null;
+    assert method.getDefinition().isNonPrivateVirtualMethod();
+    return !method.getAccessFlags().isFinal()
+        && appView.options().desugaredLibraryConfiguration.retargetMethod(method, appView) != null;
   }
 
-  private boolean dontRewrite(DexClass clazz, DexEncodedMethod method) {
-    return needsLibraryInfo() && clazz.isLibraryClass() && rewriter.dontRewrite(method.method);
+  private boolean dontRewrite(DexClassAndMethod method) {
+    return needsLibraryInfo()
+        && method.getHolder().isLibraryClass()
+        && rewriter.dontRewrite(method);
   }
 
   // Construction of actual forwarding methods.
@@ -630,13 +633,12 @@
 
   // Note: The parameter 'target' may be a public method on a class in case of desugared
   // library retargeting (See below target.isInterface check).
-  private void addForwardingMethod(DexClass targetHolder, DexEncodedMethod target, DexClass clazz) {
-    assert targetHolder != null;
+  private void addForwardingMethod(DexClassAndMethod target, DexClass clazz) {
     if (!clazz.isProgramClass()) {
       return;
     }
 
-    DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.method);
+    DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.getReference());
     if (methodOnSelf != null) {
       throw new CompilationError(
           "Attempt to add forwarding method that conflicts with existing method.",
@@ -645,13 +647,12 @@
           new MethodPosition(methodOnSelf.method.asMethodReference()));
     }
 
-    DexMethod method = target.method;
     // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
     DexMethod forwardMethod =
-        targetHolder.isInterface()
-            ? rewriter.defaultAsMethodOfCompanionClass(method)
+        target.getHolder().isInterface()
+            ? rewriter.defaultAsMethodOfCompanionClass(target)
             : appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
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 c2b151c..3dc74ac 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
@@ -44,9 +44,10 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -73,7 +74,7 @@
   // the invoke needs to be rewritten.
   private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
   // Non final virtual library methods requiring generation of emulated dispatch.
-  private final Set<DexEncodedMethod> emulatedDispatchMethods = Sets.newIdentityHashSet();
+  private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
 
   public DesugaredLibraryRetargeter(AppView<?> appView) {
     this.appView = appView;
@@ -316,9 +317,6 @@
       DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
       assert singleTarget != null;
       retarget = getRetargetLibraryMember(singleTarget.method);
-      if (retarget == null) {
-        return null;
-      }
     }
     return retarget;
   }
@@ -356,52 +354,80 @@
           DexClass typeClass = appView.definitionFor(inType);
           if (typeClass != null) {
             DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
-            List<DexEncodedMethod> found = findDexEncodedMethodsWithName(methodName, typeClass);
-            for (DexEncodedMethod encodedMethod : found) {
+            List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
+            for (DexClassAndMethod method : found) {
+              DexMethod methodReference = method.getReference();
               if (!typeClass.isFinal()) {
-                nonFinalHolderRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
-                nonFinalHolderRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
-                if (!encodedMethod.isStatic()) {
-                  if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+                nonFinalHolderRewrites.putIfAbsent(method.getName(), new ArrayList<>());
+                nonFinalHolderRewrites.get(method.getName()).add(methodReference);
+                if (!method.getAccessFlags().isStatic()) {
+                  if (isEmulatedInterfaceDispatch(method)) {
                     // In this case interface method rewriter takes care of it.
                     continue;
-                  } else if (!encodedMethod.isFinal()) {
+                  } else if (!method.getAccessFlags().isFinal()) {
                     // Virtual rewrites require emulated dispatch for inheritance.
                     // The call is rewritten to the dispatch holder class instead.
-                    handleEmulateDispatch(appView, encodedMethod);
-                    newHolder = dispatchHolderTypeFor(encodedMethod);
+                    handleEmulateDispatch(appView, method);
+                    newHolder = dispatchHolderTypeFor(method);
                   }
                 }
               }
-              DexProto proto = encodedMethod.method.proto;
-              DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
-              retargetLibraryMember.put(
-                  method, computeRetargetMethod(method, encodedMethod.isStatic(), newHolder));
+              retargetLibraryMember.put(methodReference, computeRetargetMethod(method, newHolder));
             }
           }
         }
       }
     }
 
-    private DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
-      DexItemFactory factory = appView.dexItemFactory();
-      DexProto newProto = isStatic ? method.proto : factory.prependHolderToProto(method);
-      return factory.createMethod(newHolder, newProto, method.name);
+    private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
+      // Answers true if this method is already managed through emulated interface dispatch.
+      Map<DexType, DexType> emulateLibraryInterface =
+          appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+      if (emulateLibraryInterface.isEmpty()) {
+        return false;
+      }
+      DexMethod methodToFind = method.getReference();
+
+      // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
+      // the method, answers true.
+      WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
+      while (worklist.hasNext()) {
+        DexClass clazz = worklist.next();
+        if (clazz.isInterface()
+            && emulateLibraryInterface.containsKey(clazz.getType())
+            && clazz.lookupMethod(methodToFind) != null) {
+          return true;
+        }
+        // All super types are library class, or we are doing L8 compilation.
+        clazz.forEachImmediateSupertype(
+            superType -> {
+              DexClass superClass = appView.definitionFor(superType);
+              if (superClass != null) {
+                worklist.addIfNotSeen(superClass);
+              }
+            });
+      }
+      return false;
     }
 
-    private List<DexEncodedMethod> findDexEncodedMethodsWithName(
-        DexString methodName, DexClass clazz) {
-      List<DexEncodedMethod> found = new ArrayList<>();
-      for (DexEncodedMethod encodedMethod : clazz.methods()) {
-        if (encodedMethod.method.name == methodName) {
-          found.add(encodedMethod);
-        }
-      }
-      assert found.size() > 0 : "Should have found a method (library specifications).";
+    private DexMethod computeRetargetMethod(DexClassAndMethod method, DexType newHolder) {
+      DexItemFactory factory = appView.dexItemFactory();
+      DexProto newProto =
+          method.getAccessFlags().isStatic()
+              ? method.getProto()
+              : factory.prependHolderToProto(method.getReference());
+      return factory.createMethod(newHolder, newProto, method.getName());
+    }
+
+    private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
+      List<DexClassAndMethod> found = new ArrayList<>();
+      clazz.forEachClassMethodMatching(
+          definition -> definition.getName() == methodName, found::add);
+      assert !found.isEmpty() : "Should have found a method (library specifications).";
       return found;
     }
 
-    private void handleEmulateDispatch(AppView<?> appView, DexEncodedMethod method) {
+    private void handleEmulateDispatch(AppView<?> appView, DexClassAndMethod method) {
       emulatedDispatchMethods.add(method);
       if (!appView.options().isDesugaredLibraryCompilation()) {
         // Add rewrite rules so keeps rules are correctly generated in the program.
@@ -437,8 +463,8 @@
     private void addInterfacesAndForwardingMethods(
         ExecutorService executorService, IRConverter converter) throws ExecutionException {
       assert !appView.options().isDesugaredLibraryCompilation();
-      Map<DexType, List<DexEncodedMethod>> map = Maps.newIdentityHashMap();
-      for (DexEncodedMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+      Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
+      for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
         map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
         map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
       }
@@ -470,7 +496,7 @@
     }
 
     private boolean inherit(
-        DexLibraryClass clazz, DexType typeToInherit, Set<DexEncodedMethod> retarget) {
+        DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
       DexLibraryClass current = clazz;
       while (current.type != appView.dexItemFactory().objectType) {
         if (current.type == typeToInherit) {
@@ -491,13 +517,13 @@
 
     private void addInterfacesAndForwardingMethods(
         DexProgramClass clazz,
-        List<DexEncodedMethod> methods,
+        List<DexClassAndMethod> 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.
-      for (DexEncodedMethod method : methods) {
+      for (DexClassAndMethod method : methods) {
         clazz.addExtraInterfaces(
             Collections.singletonList(new ClassTypeSignature(dispatchInterfaceTypeFor(method))));
         if (clazz.lookupVirtualMethod(method.getReference()) == null) {
@@ -508,7 +534,7 @@
       }
     }
 
-    private DexEncodedMethod createForwardingMethod(DexEncodedMethod target, DexClass clazz) {
+    private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
       // NOTE: Never add a forwarding method to methods of classes unknown or coming from
       // android.jar
       // even if this results in invalid code, these classes are never desugared.
@@ -533,7 +559,7 @@
                   | Constants.ACC_INTERFACE);
       ClassAccessFlags holderAccessFlags =
           ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-      for (DexEncodedMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+      for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
         // Dispatch interface.
         DexType interfaceType = dispatchInterfaceTypeFor(emulatedDispatchMethod);
         DexEncodedMethod itfMethod =
@@ -560,7 +586,7 @@
     }
 
     private DexEncodedMethod generateInterfaceDispatchMethod(
-        DexEncodedMethod emulatedDispatchMethod, DexType interfaceType) {
+        DexClassAndMethod emulatedDispatchMethod, DexType interfaceType) {
       MethodAccessFlags flags =
           MethodAccessFlags.fromSharedAccessFlags(
               Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
@@ -582,7 +608,7 @@
     }
 
     private DexEncodedMethod generateHolderDispatchMethod(
-        DexEncodedMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
+        DexClassAndMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
       // The method should look like:
       // static foo(rcvr, arg0, arg1) {
       //    if (rcvr instanceof interfaceType) {
@@ -613,7 +639,7 @@
   }
 
   private void reportInvalidLibrarySupertype(
-      DexLibraryClass libraryClass, Set<DexEncodedMethod> retarget) {
+      DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
     DexClass dexClass = appView.definitionFor(libraryClass.superType);
     String message;
     if (dexClass == null) {
@@ -634,15 +660,15 @@
             retarget);
   }
 
-  private DexType dispatchInterfaceTypeFor(DexEncodedMethod method) {
+  private DexType dispatchInterfaceTypeFor(DexClassAndMethod method) {
     return dispatchTypeFor(method, "dispatchInterface");
   }
 
-  private DexType dispatchHolderTypeFor(DexEncodedMethod method) {
+  private DexType dispatchHolderTypeFor(DexClassAndMethod method) {
     return dispatchTypeFor(method, "dispatchHolder");
   }
 
-  private DexType dispatchTypeFor(DexEncodedMethod method, String suffix) {
+  private DexType dispatchTypeFor(DexClassAndMethod method, String suffix) {
     String descriptor =
         "L"
             + appView
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 47793a8..18d0352 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -216,18 +216,19 @@
     return emulatedInterfaces.get(itf);
   }
 
-  private void leavingStaticInvokeToInterface(
-      DexProgramClass holder, DexEncodedMethod encodedMethod) {
+  private void leavingStaticInvokeToInterface(ProgramMethod method) {
     // When leaving static interface method invokes possibly upgrade the class file
     // version, but don't go above the initial class file version. If the input was
     // 1.7 or below, this will make a VerificationError on the input a VerificationError
     // on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
     // will remain the same.
-    if (holder.hasClassFileVersion()) {
-      encodedMethod.upgradeClassFileVersion(
-          Ordered.min(CfVersion.V1_8, holder.getInitialClassFileVersion()));
+    if (method.getHolder().hasClassFileVersion()) {
+      method
+          .getDefinition()
+          .upgradeClassFileVersion(
+              Ordered.min(CfVersion.V1_8, method.getHolder().getInitialClassFileVersion()));
     } else {
-      encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
+      method.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
     }
   }
 
@@ -235,8 +236,7 @@
   // NOTE: can be called for different methods concurrently.
   public void rewriteMethodReferences(IRCode code) {
     ProgramMethod context = code.context();
-    DexEncodedMethod encodedMethod = context.getDefinition();
-    if (synthesizedMethods.contains(encodedMethod)) {
+    if (synthesizedMethods.contains(context)) {
       return;
     }
 
@@ -252,11 +252,10 @@
           // Check that static interface methods are not referenced
           // from invoke-custom instructions via method handles.
           DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
-          reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod);
+          reportStaticInterfaceMethodHandle(context, callSite.bootstrapMethod);
           for (DexValue arg : callSite.bootstrapArgs) {
             if (arg.isDexValueMethodHandle()) {
-              reportStaticInterfaceMethodHandle(
-                  encodedMethod.method, arg.asDexValueMethodHandle().value);
+              reportStaticInterfaceMethodHandle(context, arg.asDexValueMethodHandle().value);
             }
           }
           continue;
@@ -275,9 +274,9 @@
             // exception but we can not report it as error since it can also be the intended
             // behavior.
             if (invokeStatic.getInterfaceBit()) {
-              leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
+              leavingStaticInvokeToInterface(context);
             }
-            warnMissingType(encodedMethod.method, method.holder);
+            warnMissingType(context, method.holder);
           } else if (clazz.isInterface()) {
             if (isNonDesugaredLibraryClass(clazz)) {
               // NOTE: we intentionally don't desugar static calls into static interface
@@ -302,22 +301,21 @@
                         .createMethod(
                             context.getHolder(),
                             factory,
-                            syntheticMethodBuilder -> {
-                              syntheticMethodBuilder
-                                  .setProto(method.proto)
-                                  .setAccessFlags(
-                                      MethodAccessFlags.fromSharedAccessFlags(
-                                          Constants.ACC_PUBLIC
-                                              | Constants.ACC_STATIC
-                                              | Constants.ACC_SYNTHETIC,
-                                          false))
-                                  .setCode(
-                                      m ->
-                                          ForwardMethodBuilder.builder(factory)
-                                              .setStaticTarget(method, true)
-                                              .setStaticSource(m)
-                                              .build());
-                            });
+                            syntheticMethodBuilder ->
+                                syntheticMethodBuilder
+                                    .setProto(method.proto)
+                                    .setAccessFlags(
+                                        MethodAccessFlags.fromSharedAccessFlags(
+                                            Constants.ACC_PUBLIC
+                                                | Constants.ACC_STATIC
+                                                | Constants.ACC_SYNTHETIC,
+                                            false))
+                                    .setCode(
+                                        m ->
+                                            ForwardMethodBuilder.builder(factory)
+                                                .setStaticTarget(method, true)
+                                                .setStaticSource(m)
+                                                .build()));
                 instructions.replaceCurrentInstruction(
                     new InvokeStatic(
                         newProgramMethod.getReference(),
@@ -331,7 +329,7 @@
                 }
               } else {
                 // When leaving static interface method invokes upgrade the class file version.
-                encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
+                context.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
               }
             } else {
               instructions.replaceCurrentInstruction(
@@ -341,7 +339,7 @@
           } else {
             assert !clazz.isInterface();
             if (invokeStatic.getInterfaceBit()) {
-              leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
+              leavingStaticInvokeToInterface(context);
             }
           }
           continue;
@@ -355,7 +353,7 @@
             // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
             // exception but we can not report it as error since it can also be the intended
             // behavior.
-            warnMissingType(encodedMethod.method, invokedMethod.holder);
+            warnMissingType(context, invokedMethod.holder);
           } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
             // NOTE: we intentionally don't desugar super calls into interface methods
             // coming from android.jar since it is only possible in case v24+ version
@@ -366,15 +364,13 @@
             //
             // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
-            DexMethod amendedMethod =
-                amendDefaultMethod(
-                    appInfo.definitionFor(encodedMethod.getHolderType()), invokedMethod);
+            DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
                     invokeSuper.outValue(), invokeSuper.arguments()));
           } else {
-            DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-            if (dexType == null) {
+            DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+            if (emulatedItf == null) {
               if (clazz.isInterface()
                   && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
                 DexClassAndMethod target =
@@ -411,9 +407,9 @@
                           superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
                   DexMethod companionMethod =
                       factory.createMethod(
-                          getCompanionClassType(dexType),
+                          getCompanionClassType(emulatedItf),
                           factory.protoWithDifferentFirstParameter(
-                              originalCompanionMethod.proto, dexType),
+                              originalCompanionMethod.proto, emulatedItf),
                           originalCompanionMethod.name);
                   instructions.replaceCurrentInstruction(
                       new InvokeStatic(
@@ -436,15 +432,17 @@
             continue;
           }
 
-          DexClass clazz = appInfo.definitionForHolder(method);
+          DexClass clazz = appInfo.definitionForHolder(method, context);
           if (clazz == null) {
             // Report missing class since we don't know if it is an interface.
-            warnMissingType(encodedMethod.method, method.holder);
+            warnMissingType(context, method.holder);
           } else if (clazz.isInterface()) {
             if (clazz.isLibraryClass()) {
-              throw new CompilationError("Unexpected call to a private method " +
-                  "defined in library class " + clazz.toSourceString(),
-                  getMethodOrigin(encodedMethod.method));
+              throw new CompilationError(
+                  "Unexpected call to a private method "
+                      + "defined in library class "
+                      + clazz.toSourceString(),
+                  getMethodOrigin(context.getReference()));
             }
             DexEncodedMethod directTarget = clazz.lookupMethod(method);
             if (directTarget != null) {
@@ -487,8 +485,8 @@
         if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
           InvokeMethod invokeMethod = instruction.asInvokeMethod();
           DexMethod invokedMethod = invokeMethod.getInvokedMethod();
-          DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-          if (dexType != null) {
+          DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+          if (emulatedItf != null) {
             // The call potentially ends up in a library class, in which case we need to rewrite,
             // since the code may be in the desugared library.
             SingleResolutionResult resolution =
@@ -500,7 +498,7 @@
                 && (resolution.getResolvedHolder().isLibraryClass()
                     || appView.options().isDesugaredLibraryCompilation())) {
               rewriteCurrentInstructionToEmulatedInterfaceCall(
-                  dexType, invokedMethod, invokeMethod, instructions);
+                  emulatedItf, invokedMethod, invokeMethod, instructions);
             }
           }
         }
@@ -547,12 +545,13 @@
       DexMethod invokedMethod,
       InvokeMethod invokeMethod,
       InstructionListIterator instructions) {
-    DexEncodedMethod defaultMethod = appView.definitionFor(emulatedItf).lookupMethod(invokedMethod);
-    if (defaultMethod != null && !dontRewrite(defaultMethod.method)) {
-      assert !defaultMethod.isAbstract();
+    DexClassAndMethod defaultMethod =
+        appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
+    if (defaultMethod != null && !dontRewrite(defaultMethod)) {
+      assert !defaultMethod.getAccessFlags().isAbstract();
       instructions.replaceCurrentInstruction(
           new InvokeStatic(
-              emulateInterfaceLibraryMethod(invokedMethod, emulatedItf, factory),
+              emulateInterfaceLibraryMethod(defaultMethod),
               invokeMethod.outValue(),
               invokeMethod.arguments()));
     }
@@ -570,10 +569,11 @@
     return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
   }
 
-  boolean dontRewrite(DexMethod method) {
+  boolean dontRewrite(DexClassAndMethod method) {
     for (Pair<DexType, DexString> dontRewrite :
         options.desugaredLibraryConfiguration.getDontRewriteInvocation()) {
-      if (method.holder == dontRewrite.getFirst() && method.name == dontRewrite.getSecond()) {
+      if (method.getHolderType() == dontRewrite.getFirst()
+          && method.getName() == dontRewrite.getSecond()) {
         return true;
       }
     }
@@ -666,87 +666,82 @@
     return false;
   }
 
-  private static DexMethod emulateInterfaceLibraryMethod(
-      DexMethod method, DexType holder, DexItemFactory factory) {
+  private DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
     return factory.createMethod(
-        getEmulateLibraryInterfaceClassType(holder, factory),
-        factory.prependTypeToProto(holder, method.proto),
-        factory.createString(method.name.toString()));
+        getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
+        factory.prependTypeToProto(method.getHolderType(), method.getProto()),
+        method.getName());
   }
 
   private DexProgramClass synthesizeEmulateInterfaceLibraryClass(
       DexProgramClass theInterface, Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
     List<DexEncodedMethod> emulationMethods = new ArrayList<>();
-    for (DexEncodedMethod method : theInterface.methods()) {
-      if (method.isDefaultMethod()) {
-        DexMethod libraryMethod =
-            factory.createMethod(
-                emulatedInterfaces.get(theInterface.type), method.method.proto, method.method.name);
-        DexMethod originalCompanionMethod =
-            method.isStatic()
-                ? staticAsMethodOfCompanionClass(method.method)
-                : instanceAsMethodOfCompanionClass(method.method, DEFAULT_METHOD_PREFIX, factory);
-        DexMethod companionMethod =
-            factory.createMethod(
-                getCompanionClassType(theInterface.type),
-                originalCompanionMethod.proto,
-                originalCompanionMethod.name);
+    theInterface.forEachProgramMethodMatching(
+        DexEncodedMethod::isDefaultMethod,
+        method -> {
+          DexMethod libraryMethod =
+              method.getReference().withHolder(emulatedInterfaces.get(theInterface.type), factory);
+          DexMethod companionMethod =
+              method.getAccessFlags().isStatic()
+                  ? staticAsMethodOfCompanionClass(method)
+                  : defaultAsMethodOfCompanionClass(method);
 
-        // To properly emulate the library interface call, we need to compute the interfaces
-        // inheriting from the interface and manually implement the dispatch with instance of.
-        // The list guarantees that an interface is always after interfaces it extends,
-        // hence reverse iteration.
-        List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
-        List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
-        // In practice, there is usually a single case (except for tests),
-        // so we do not bother to make the following loop more clever.
-        Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-            options.desugaredLibraryConfiguration.getRetargetCoreLibMember();
-        for (DexString methodName : retargetCoreLibMember.keySet()) {
-          if (method.method.name == methodName) {
-            for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-              DexClass inClass = appView.definitionFor(inType);
-              if (inClass != null && implementsInterface(inClass, theInterface.type)) {
-                extraDispatchCases.add(
-                    new Pair<>(
-                        inType,
-                        factory.createMethod(
-                            retargetCoreLibMember.get(methodName).get(inType),
-                            factory.protoWithDifferentFirstParameter(
-                                originalCompanionMethod.proto, inType),
-                            method.method.name)));
+          // To properly emulate the library interface call, we need to compute the interfaces
+          // inheriting from the interface and manually implement the dispatch with instance of.
+          // The list guarantees that an interface is always after interfaces it extends,
+          // hence reverse iteration.
+          List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
+          List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
+          // In practice, there is usually a single case (except for tests),
+          // so we do not bother to make the following loop more clever.
+          Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+              options.desugaredLibraryConfiguration.getRetargetCoreLibMember();
+          for (DexString methodName : retargetCoreLibMember.keySet()) {
+            if (method.getName() == methodName) {
+              for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+                DexClass inClass = appView.definitionFor(inType);
+                if (inClass != null && implementsInterface(inClass, theInterface.type)) {
+                  extraDispatchCases.add(
+                      new Pair<>(
+                          inType,
+                          factory.createMethod(
+                              retargetCoreLibMember.get(methodName).get(inType),
+                              factory.protoWithDifferentFirstParameter(
+                                  companionMethod.proto, inType),
+                              method.getName())));
+                }
               }
             }
           }
-        }
-        if (subInterfaces != null) {
-          for (int i = subInterfaces.size() - 1; i >= 0; i--) {
-            DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
-            assert subInterfaceClass != null; // Else computation of subInterface would have failed.
-            // if the method is implemented, extra dispatch is required.
-            DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.method);
-            if (result != null && !result.isAbstract()) {
-              extraDispatchCases.add(
-                  new Pair<>(
-                      subInterfaceClass.type,
-                      factory.createMethod(
-                          getCompanionClassType(subInterfaceClass.type),
-                          factory.protoWithDifferentFirstParameter(
-                              originalCompanionMethod.proto, subInterfaceClass.type),
-                          originalCompanionMethod.name)));
+          if (subInterfaces != null) {
+            for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+              DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
+              assert subInterfaceClass
+                  != null; // Else computation of subInterface would have failed.
+              // if the method is implemented, extra dispatch is required.
+              DexEncodedMethod result =
+                  subInterfaceClass.lookupVirtualMethod(method.getReference());
+              if (result != null && !result.isAbstract()) {
+                extraDispatchCases.add(
+                    new Pair<>(
+                        subInterfaceClass.type,
+                        factory.createMethod(
+                            getCompanionClassType(subInterfaceClass.type),
+                            factory.protoWithDifferentFirstParameter(
+                                companionMethod.proto, subInterfaceClass.type),
+                            companionMethod.name)));
+              }
             }
           }
-        }
-        emulationMethods.add(
-            DexEncodedMethod.toEmulateDispatchLibraryMethod(
-                method.getHolderType(),
-                emulateInterfaceLibraryMethod(method.method, method.getHolderType(), factory),
-                companionMethod,
-                libraryMethod,
-                extraDispatchCases,
-                appView));
-      }
-    }
+          emulationMethods.add(
+              DexEncodedMethod.toEmulateDispatchLibraryMethod(
+                  method.getHolderType(),
+                  emulateInterfaceLibraryMethod(method),
+                  companionMethod,
+                  libraryMethod,
+                  extraDispatchCases,
+                  appView));
+        });
     if (emulationMethods.isEmpty()) {
       return null;
     }
@@ -799,17 +794,17 @@
     return factory.createSynthesizedType(elTypeDescriptor);
   }
 
-  private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
+  private void reportStaticInterfaceMethodHandle(ProgramMethod context, DexMethodHandle handle) {
     if (handle.type.isInvokeStatic()) {
       DexClass holderClass = appView.definitionFor(handle.asMethod().holder);
       // NOTE: If the class definition is missing we can't check. Let it be handled as any other
       // missing call target.
       if (holderClass == null) {
-        warnMissingType(referencedFrom, handle.asMethod().holder);
+        warnMissingType(context, handle.asMethod().holder);
       } else if (holderClass.isInterface()) {
         throw new Unimplemented(
             "Desugaring of static interface method handle in `"
-                + referencedFrom.toSourceString()
+                + context.toSourceString()
                 + "` is not yet supported.");
       }
     }
@@ -864,6 +859,10 @@
   }
 
   // Represent a static interface method as a method of companion class.
+  final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
+    return staticAsMethodOfCompanionClass(method.getReference());
+  }
+
   final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
     // No changes for static methods.
     return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
@@ -913,6 +912,10 @@
     return defaultAsMethodOfCompanionClass(method, factory);
   }
 
+  DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
+    return defaultAsMethodOfCompanionClass(method.getReference(), factory);
+  }
+
   // Represent a private instance interface method as a method of companion class.
   static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
     // Add an implicit argument to represent the receiver.
@@ -1135,37 +1138,6 @@
     return true;
   }
 
-  public static boolean isEmulatedInterfaceDispatch(AppView<?> appView, DexEncodedMethod method) {
-    // Answers true if this method is already managed through emulated interface dispatch.
-    Map<DexType, DexType> emulateLibraryInterface =
-        appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
-    if (emulateLibraryInterface.isEmpty()) {
-      return false;
-    }
-    DexMethod methodToFind = method.method;
-
-    // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
-    // the method, answers true.
-    LinkedList<DexType> workList = new LinkedList<>();
-    workList.add(methodToFind.holder);
-    while (!workList.isEmpty()) {
-      DexType dexType = workList.removeFirst();
-      DexClass dexClass = appView.definitionFor(dexType);
-      assert dexClass != null; // It is a library class, or we are doing L8 compilation.
-      if (dexClass.isInterface() && emulateLibraryInterface.containsKey(dexType)) {
-        DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(methodToFind);
-        if (dexEncodedMethod != null) {
-          return true;
-        }
-      }
-      Collections.addAll(workList, dexClass.interfaces.values);
-      if (dexClass.superType != appView.dexItemFactory().objectType) {
-        workList.add(dexClass.superType);
-      }
-    }
-    return false;
-  }
-
   private boolean shouldIgnoreFromReports(DexType missing) {
     return appView.rewritePrefix.hasRewrittenType(missing, appView)
         || missing.isD8R8SynthesizedClassType()
@@ -1187,13 +1159,13 @@
     options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
   }
 
-  private void warnMissingType(DexMethod referencedFrom, DexType missing) {
+  private void warnMissingType(ProgramMethod context, DexType missing) {
     // Companion/Emulated interface/Conversion classes for desugared library won't be missing,
     // they are in the desugared library.
     if (shouldIgnoreFromReports(missing)) {
       return;
     }
-    DexMethod method = appView.graphLens().getOriginalMethodSignature(referencedFrom);
+    DexMethod method = appView.graphLens().getOriginalMethodSignature(context.getReference());
     Origin origin = getMethodOrigin(method);
     MethodPosition position = new MethodPosition(method.asMethodReference());
     options.warningMissingTypeForDesugar(origin, position, missing, method);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 985090a..db914a4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -63,12 +63,15 @@
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
 import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.io.PrintStream;
@@ -990,7 +993,7 @@
       DexType libraryType,
       DexType invalidSuperType,
       String message,
-      Set<DexEncodedMethod> retarget) {
+      DexClassAndMethodSet retarget) {
     if (invalidLibraryClasses.add(invalidSuperType)) {
       reporter.warning(
           new InvalidLibrarySuperclassDiagnostic(
@@ -998,7 +1001,9 @@
               Reference.classFromDescriptor(libraryType.toDescriptorString()),
               Reference.classFromDescriptor(invalidSuperType.toDescriptorString()),
               message,
-              ListUtils.map(retarget, method -> method.getReference().asMethodReference())));
+              Lists.newArrayList(
+                  Iterables.transform(
+                      retarget, method -> method.getReference().asMethodReference()))));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index ebbef95..cd1c0a0 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -15,6 +15,16 @@
 
 public class IterableUtils {
 
+  public static <S, T> boolean any(
+      Iterable<S> iterable, Function<S, T> transform, Predicate<T> predicate) {
+    for (S element : iterable) {
+      if (predicate.test(transform.apply(element))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public static <T> Iterable<T> append(Iterable<T> iterable, T element) {
     return Iterables.concat(iterable, singleton(element));
   }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java
new file mode 100644
index 0000000..ad7c3fe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java
@@ -0,0 +1,66 @@
+// 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.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public class DexClassAndMethodSet extends DexClassAndMethodSetBase<DexClassAndMethod> {
+
+  private static final DexClassAndMethodSet EMPTY = new DexClassAndMethodSet(ImmutableMap::of);
+
+  protected DexClassAndMethodSet(
+      Supplier<? extends Map<DexMethod, DexClassAndMethod>> backingFactory) {
+    super(backingFactory);
+  }
+
+  protected DexClassAndMethodSet(
+      Supplier<? extends Map<DexMethod, DexClassAndMethod>> backingFactory,
+      Map<DexMethod, DexClassAndMethod> backing) {
+    super(backingFactory, backing);
+  }
+
+  public static DexClassAndMethodSet create() {
+    return new DexClassAndMethodSet(IdentityHashMap::new);
+  }
+
+  public static DexClassAndMethodSet create(int capacity) {
+    return new DexClassAndMethodSet(IdentityHashMap::new, new IdentityHashMap<>(capacity));
+  }
+
+  public static DexClassAndMethodSet create(DexClassAndMethod element) {
+    DexClassAndMethodSet result = create();
+    result.add(element);
+    return result;
+  }
+
+  public static DexClassAndMethodSet create(DexClassAndMethodSet methodSet) {
+    DexClassAndMethodSet newMethodSet = create();
+    newMethodSet.addAll(methodSet);
+    return newMethodSet;
+  }
+
+  public static DexClassAndMethodSet createConcurrent() {
+    return new DexClassAndMethodSet(ConcurrentHashMap::new);
+  }
+
+  public static DexClassAndMethodSet createLinked() {
+    return new DexClassAndMethodSet(LinkedHashMap::new);
+  }
+
+  public static DexClassAndMethodSet empty() {
+    return EMPTY;
+  }
+
+  public void addAll(DexClassAndMethodSet methods) {
+    backing.putAll(methods.backing);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
new file mode 100644
index 0000000..797d8b6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -0,0 +1,87 @@
+// 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.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public abstract class DexClassAndMethodSetBase<T extends DexClassAndMethod> implements Iterable<T> {
+
+  protected final Map<DexMethod, T> backing;
+  protected final Supplier<? extends Map<DexMethod, T>> backingFactory;
+
+  protected DexClassAndMethodSetBase(Supplier<? extends Map<DexMethod, T>> backingFactory) {
+    this(backingFactory, backingFactory.get());
+  }
+
+  protected DexClassAndMethodSetBase(
+      Supplier<? extends Map<DexMethod, T>> backingFactory, Map<DexMethod, T> backing) {
+    this.backing = backing;
+    this.backingFactory = backingFactory;
+  }
+
+  public boolean add(T method) {
+    T existing = backing.put(method.getReference(), method);
+    assert existing == null || existing.isStructurallyEqualTo(method);
+    return existing == null;
+  }
+
+  public void addAll(Iterable<T> methods) {
+    methods.forEach(this::add);
+  }
+
+  public boolean contains(DexEncodedMethod method) {
+    return backing.containsKey(method.getReference());
+  }
+
+  public boolean contains(T method) {
+    return backing.containsKey(method.getReference());
+  }
+
+  public void clear() {
+    backing.clear();
+  }
+
+  public boolean isEmpty() {
+    return backing.isEmpty();
+  }
+
+  @Override
+  public Iterator<T> iterator() {
+    return backing.values().iterator();
+  }
+
+  public boolean remove(DexMethod method) {
+    T existing = backing.remove(method);
+    return existing != null;
+  }
+
+  public boolean remove(DexEncodedMethod method) {
+    return remove(method.getReference());
+  }
+
+  public int size() {
+    return backing.size();
+  }
+
+  public Stream<T> stream() {
+    return backing.values().stream();
+  }
+
+  public Set<DexEncodedMethod> toDefinitionSet() {
+    assert backing instanceof IdentityHashMap;
+    Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet();
+    forEach(method -> definitions.add(method.getDefinition()));
+    return definitions;
+  }
+}
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 f058bf1..d36d207 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
@@ -11,32 +11,24 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Supplier;
-import java.util.stream.Stream;
 
-public class ProgramMethodSet implements Iterable<ProgramMethod> {
+public class ProgramMethodSet extends DexClassAndMethodSetBase<ProgramMethod> {
 
   private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap::of);
 
-  private final Map<DexMethod, ProgramMethod> backing;
-  private final Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory;
-
   protected ProgramMethodSet(Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory) {
-    this(backingFactory, backingFactory.get());
+    super(backingFactory);
   }
 
   protected ProgramMethodSet(
       Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory,
       Map<DexMethod, ProgramMethod> backing) {
-    this.backing = backing;
-    this.backingFactory = backingFactory;
+    super(backingFactory, backing);
   }
 
   public static ProgramMethodSet create() {
@@ -71,16 +63,6 @@
     return EMPTY;
   }
 
-  public boolean add(ProgramMethod method) {
-    ProgramMethod existing = backing.put(method.getReference(), method);
-    assert existing == null || existing.isStructurallyEqualTo(method);
-    return existing == null;
-  }
-
-  public void addAll(Iterable<ProgramMethod> methods) {
-    methods.forEach(this::add);
-  }
-
   public void addAll(ProgramMethodSet methods) {
     backing.putAll(methods.backing);
   }
@@ -89,36 +71,6 @@
     return add(new ProgramMethod(clazz, definition));
   }
 
-  public boolean contains(DexEncodedMethod method) {
-    return backing.containsKey(method.getReference());
-  }
-
-  public boolean contains(ProgramMethod method) {
-    return backing.containsKey(method.getReference());
-  }
-
-  public void clear() {
-    backing.clear();
-  }
-
-  public boolean isEmpty() {
-    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;
-  }
-
-  public boolean remove(DexEncodedMethod method) {
-    return remove(method.getReference());
-  }
-
   public ProgramMethodSet rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
     ProgramMethodSet rewritten = new ProgramMethodSet(backingFactory);
     forEach(
@@ -130,19 +82,4 @@
         });
     return rewritten;
   }
-
-  public int size() {
-    return backing.size();
-  }
-
-  public Stream<ProgramMethod> stream() {
-    return backing.values().stream();
-  }
-
-  public Set<DexEncodedMethod> toDefinitionSet() {
-    assert backing instanceof IdentityHashMap;
-    Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet();
-    forEach(method -> definitions.add(method.getDefinition()));
-    return definitions;
-  }
 }
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index fd57fad..c4de6e3 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -5,6 +5,7 @@
 
 import adb
 import apk_masseur
+import as_utils
 import compiledump
 import gradle
 import optparse