Revert "Split BackporterMethodRewriter and RetargetLibraryMember"

This reverts commit 6b15b7762f894b911474790f9d4989d90f6cb217.

Reason for revert: failing tests

Change-Id: I1fcd541768a3c4d628107192ca5d2362b73bd94c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b843181..100b5aa 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -34,7 +34,7 @@
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.NestedPrivateMethodLense;
 import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -278,7 +278,7 @@
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
       if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
-        DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView);
+        BackportedMethodRewriter.checkForAssumedLibraryTypes(appView);
       }
 
       List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 01c5298..4b8c6d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
 import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
@@ -45,7 +44,8 @@
   private String toStringCache = null;
 
   DexType(DexString descriptor) {
-    assert !descriptor.toString().contains(".") : "Malformed descriptor: " + descriptor.toString();
+    assert !descriptor.toString().contains(".")
+        : "Malformed descriptor: " + descriptor.toString();
     this.descriptor = descriptor;
   }
 
@@ -145,12 +145,11 @@
   }
 
   @Override
-  public void collectIndexedItems(
-      IndexedItemCollection collection, DexMethod method, int instructionOffset) {
+  public void collectIndexedItems(IndexedItemCollection collection,
+      DexMethod method, int instructionOffset) {
     if (collection.addType(this)) {
-      collection
-          .getRenamedDescriptor(this)
-          .collectIndexedItems(collection, method, instructionOffset);
+      collection.getRenamedDescriptor(this).collectIndexedItems(collection, method,
+          instructionOffset);
     }
   }
 
@@ -273,7 +272,6 @@
         || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
         || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
         || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)
-        || name.contains(DesugaredLibraryRetargeter.DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX)
         || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME)
         || oldSynthesizedName(name);
   }
@@ -300,17 +298,17 @@
   public int elementSizeForPrimitiveArrayType() {
     assert isPrimitiveArrayType();
     switch (descriptor.content[1]) {
-      case 'Z': // boolean
-      case 'B': // byte
+      case 'Z':  // boolean
+      case 'B':  // byte
         return 1;
-      case 'S': // short
-      case 'C': // char
+      case 'S':  // short
+      case 'C':  // char
         return 2;
-      case 'I': // int
-      case 'F': // float
+      case 'I':  // int
+      case 'F':  // float
         return 4;
-      case 'J': // long
-      case 'D': // double
+      case 'J':  // long
+      case 'D':  // double
         return 8;
       default:
         throw new Unreachable("Not array of primitives '" + descriptor + "'");
@@ -330,11 +328,8 @@
     if (leadingSquareBrackets == 0) {
       return this;
     }
-    DexString newDesc =
-        dexItemFactory.createString(
-            descriptor.size - leadingSquareBrackets,
-            Arrays.copyOfRange(
-                descriptor.content, leadingSquareBrackets, descriptor.content.length));
+    DexString newDesc = dexItemFactory.createString(descriptor.size - leadingSquareBrackets,
+        Arrays.copyOfRange(descriptor.content, leadingSquareBrackets, descriptor.content.length));
     return dexItemFactory.createType(newDesc);
   }
 
@@ -369,10 +364,8 @@
 
   public DexType toArrayElementType(DexItemFactory dexItemFactory) {
     assert this.isArrayType();
-    DexString newDesc =
-        dexItemFactory.createString(
-            descriptor.size - 1,
-            Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
+    DexString newDesc = dexItemFactory.createString(descriptor.size - 1,
+        Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
     return dexItemFactory.createType(newDesc);
   }
 
@@ -383,8 +376,7 @@
     if (lastSeparator == -1) {
       return packagePart ? "" : descriptor.substring(1, descriptor.length() - 1);
     } else {
-      return packagePart
-          ? descriptor.substring(1, lastSeparator)
+      return packagePart ? descriptor.substring(1, lastSeparator)
           : descriptor.substring(lastSeparator + 1, descriptor.length() - 1);
     }
   }
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 4c820ff..4ac8995 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
@@ -45,7 +45,6 @@
 import com.android.tools.r8.ir.desugar.D8NestBasedAccessDesugaring;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
@@ -144,7 +143,6 @@
   private final InterfaceMethodRewriter interfaceMethodRewriter;
   private final TwrCloseResourceRewriter twrCloseResourceRewriter;
   private final BackportedMethodRewriter backportedMethodRewriter;
-  private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
   private final LambdaMerger lambdaMerger;
   private final ClassInliner classInliner;
   private final ClassStaticizer classStaticizer;
@@ -219,16 +217,13 @@
             .collect(Collectors.toList());
     if (options.isDesugaredLibraryCompilation()) {
       // Specific L8 Settings.
-      // DesugaredLibraryRetargeter is needed for retarget core library members and backports.
+      // BackportedMethodRewriter is needed for retarget core library members and backports.
       // InterfaceMethodRewriter is needed for emulated interfaces.
       // LambdaRewriter is needed because if it is missing there are invoke custom on
       // default/static interface methods, and this is not supported by the compiler.
       // DesugaredLibraryAPIConverter is here to duplicate APIs.
       // The rest is nulled out. In addition the rewriting logic fails without lambda rewriting.
-      this.desugaredLibraryRetargeter =
-          options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
-              ? null
-              : new DesugaredLibraryRetargeter(appView);
+      this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
       this.interfaceMethodRewriter =
           options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
               ? null
@@ -236,7 +231,6 @@
       this.lambdaRewriter = new LambdaRewriter(appView);
       this.desugaredLibraryAPIConverter =
           new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
-      this.backportedMethodRewriter = null;
       this.twrCloseResourceRewriter = null;
       this.lambdaMerger = null;
       this.covariantReturnTypeAnnotationTransformer = null;
@@ -274,10 +268,6 @@
             ? new TwrCloseResourceRewriter(appView, this)
             : null;
     this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
-    this.desugaredLibraryRetargeter =
-        options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
-            ? null
-            : new DesugaredLibraryRetargeter(appView);
     this.covariantReturnTypeAnnotationTransformer =
         options.processCovariantReturnTypeAnnotations
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -460,16 +450,7 @@
 
   private void synthesizeJava8UtilityClass(
       Builder<?> builder, ExecutorService executorService) throws ExecutionException {
-    if (backportedMethodRewriter != null) {
-      backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
-    }
-  }
-
-  private void synthesizeRetargetClass(Builder<?> builder, ExecutorService executorService)
-      throws ExecutionException {
-    if (desugaredLibraryRetargeter != null) {
-      desugaredLibraryRetargeter.synthesizeRetargetClasses(builder, executorService, this);
-    }
+    backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
   }
 
   private void synthesizeEnumUnboxingUtilityClass(
@@ -501,8 +482,6 @@
     desugarInterfaceMethods(builder, ExcludeDexResources, executor);
     synthesizeTwrCloseResourceUtilityClass(builder, executor);
     synthesizeJava8UtilityClass(builder, executor);
-    synthesizeRetargetClass(builder, executor);
-
     processCovariantReturnTypeAnnotations(builder);
     generateDesugaredLibraryAPIWrappers(builder, executor);
 
@@ -768,7 +747,6 @@
     printPhase("Utility classes synthesis");
     synthesizeTwrCloseResourceUtilityClass(builder, executorService);
     synthesizeJava8UtilityClass(builder, executorService);
-    synthesizeRetargetClass(builder, executorService);
     handleSynthesizedClassMapping(builder);
     synthesizeEnumUnboxingUtilityClass(builder, executorService);
 
@@ -1392,20 +1370,9 @@
       codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
       timing.end();
     }
-
-    if (desugaredLibraryRetargeter != null) {
-      // The desugaredLibraryRetargeter should run before backportedMethodRewriter to be able to
-      // perform backport rewriting before the methods can be retargeted.
-      timing.begin("Retarget library methods");
-      desugaredLibraryRetargeter.desugar(code);
-      timing.end();
-    }
-
-    if (backportedMethodRewriter != null) {
-      timing.begin("Rewrite backport methods");
-      backportedMethodRewriter.desugar(code);
-      timing.end();
-    }
+    timing.begin("Rewrite backport methods");
+    backportedMethodRewriter.desugar(code);
+    timing.end();
 
     timing.begin("Desugar string concat");
     stringConcatRewriter.desugarStringConcats(method.method, code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index c56524e..50b068c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
@@ -30,6 +31,7 @@
 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.ResolutionResult;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -52,10 +54,13 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.IdentityHashMap;
@@ -117,9 +122,6 @@
       BackportedMethodRewriter.RewritableMethods rewritableMethods =
           new BackportedMethodRewriter.RewritableMethods(options, appView);
       rewritableMethods.visit(methods::add);
-      DesugaredLibraryRetargeter desugaredLibraryRetargeter =
-          new DesugaredLibraryRetargeter(appView);
-      desugaredLibraryRetargeter.visit(methods::add);
       return methods;
     } catch (ExecutionException e) {
       throw unwrapExecutionException(e);
@@ -144,10 +146,49 @@
           && !invoke.isInvokeStatic()) {
         continue;
       }
-
       MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod());
       if (provider == null) {
-        continue;
+        if (!rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
+          continue;
+        }
+        // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
+        ResolutionResult resolutionResult =
+            appView
+                .appInfo()
+                .resolveMethod(invoke.getInvokedMethod().holder, invoke.getInvokedMethod());
+        if (resolutionResult.isFailedResolution()) {
+          continue;
+        }
+        DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+        assert singleTarget != null;
+        provider = getMethodProviderOrNull(singleTarget.method);
+        if (provider == null) {
+          continue;
+        }
+      }
+
+      // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
+      // infinite loops. We do direct resolution. This is a very uncommon case.
+      if (invoke.isInvokeSuper()
+          && rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
+        DexEncodedMethod dexEncodedMethod =
+            appView
+                .appInfo()
+                .withClassHierarchy()
+                .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
+        // Final methods can be rewritten as a normal invoke.
+        if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
+          DexMethod retargetMethod =
+              appView
+                  .options()
+                  .desugaredLibraryConfiguration
+                  .retargetMethod(dexEncodedMethod.method, appView);
+          if (retargetMethod != null) {
+            iterator.replaceCurrentInstruction(
+                new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
+          }
+          continue;
+        }
       }
 
       provider.rewriteInvoke(invoke, iterator, code, appView, affectedValues);
@@ -181,6 +222,11 @@
     if (!enabled) {
       return;
     }
+    if (appView.options().isDesugaredLibraryCompilation()) {
+      synthesizeEmulatedDispatchMethods(builder);
+    } else {
+      addInterfacesAndForwardingMethods(executorService);
+    }
     if (holders.isEmpty()) {
       return;
     }
@@ -197,9 +243,8 @@
       }
     }
 
-    MethodAccessFlags flags =
-        MethodAccessFlags.fromSharedAccessFlags(
-            Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
+    MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
+        Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
     ClassAccessFlags classAccessFlags =
         ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
     // Generate the utility classes in a loop since utility classes can require the
@@ -217,12 +262,7 @@
       Code code = provider.generateTemplateMethod(appView.options(), method);
       DexEncodedMethod dexEncodedMethod =
           new DexEncodedMethod(
-              method,
-              flags,
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              code,
-              true);
+              method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
       boolean addToMainDexList =
           referencingClasses.stream()
               .anyMatch(clazz -> appView.appInfo().isInMainDexList(clazz.type));
@@ -233,27 +273,140 @@
               method.holder,
               dexEncodedMethod,
               "java8 methods utility class",
-              addToMainDexList,
-              appView);
+              addToMainDexList);
       // The following may add elements to methodsProviders.
       converter.optimizeSynthesizedClass(utilityClass, executorService);
     }
   }
 
-  static DexProgramClass synthesizeClassWithUniqueMethod(
+  private void addInterfacesAndForwardingMethods(ExecutorService executorService)
+      throws ExecutionException {
+    assert !appView.options().isDesugaredLibraryCompilation();
+    Map<DexType, List<DexMethod>> map = Maps.newIdentityHashMap();
+    for (DexMethod emulatedDispatchMethod : rewritableMethods.getEmulatedDispatchMethods()) {
+      map.putIfAbsent(emulatedDispatchMethod.holder, new ArrayList<>(1));
+      map.get(emulatedDispatchMethod.holder).add(emulatedDispatchMethod);
+    }
+    List<DexEncodedMethod> addedMethods = new ArrayList<>();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (clazz.superType == null) {
+        assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
+        continue;
+      }
+      DexClass dexClass = 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)) {
+            addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
+          }
+        }
+      }
+    }
+    if (addedMethods.isEmpty()) {
+      return;
+    }
+    converter.processMethodsConcurrently(addedMethods, executorService);
+  }
+
+  private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) {
+    DexLibraryClass current = clazz;
+    while (current.type != appView.dexItemFactory().objectType) {
+      if (current.type == typeToInherit) {
+        return true;
+      }
+      current = appView.definitionFor(current.superType).asLibraryClass();
+    }
+    return false;
+  }
+
+  private List<DexEncodedMethod> addInterfacesAndForwardingMethods(
+      DexProgramClass clazz, List<DexMethod> dexMethods) {
+    // BackportedMethodRewriter 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<DexEncodedMethod> newForwardingMethods = new ArrayList<>();
+    for (DexMethod dexMethod : dexMethods) {
+      DexType[] newInterfaces = Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1);
+      newInterfaces[newInterfaces.length - 1] =
+          BackportedMethodRewriter.dispatchInterfaceTypeFor(appView, dexMethod);
+      clazz.interfaces = new DexTypeList(newInterfaces);
+      DexEncodedMethod dexEncodedMethod = clazz.lookupVirtualMethod(dexMethod);
+      if (dexEncodedMethod == null) {
+        DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz);
+        clazz.addVirtualMethod(newMethod);
+        newForwardingMethods.add(newMethod);
+      }
+    }
+    return newForwardingMethods;
+  }
+
+  private DexEncodedMethod createForwardingMethod(DexMethod 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.
+    // In desugared library, emulated interface methods can be overridden by retarget lib members.
+    DexMethod forwardMethod =
+        appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
+    assert forwardMethod != null && forwardMethod != target;
+    return DexEncodedMethod.createDesugaringForwardingMethod(
+        appView.definitionFor(target), clazz, forwardMethod, appView.dexItemFactory());
+  }
+
+  private void synthesizeEmulatedDispatchMethods(Builder<?> builder) {
+    assert appView.options().isDesugaredLibraryCompilation();
+    if (rewritableMethods.getEmulatedDispatchMethods().isEmpty()) {
+      return;
+    }
+    ClassAccessFlags itfAccessFlags =
+        ClassAccessFlags.fromSharedAccessFlags(
+            Constants.ACC_PUBLIC
+                | Constants.ACC_SYNTHETIC
+                | Constants.ACC_ABSTRACT
+                | Constants.ACC_INTERFACE);
+    ClassAccessFlags holderAccessFlags =
+        ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
+    for (DexMethod emulatedDispatchMethod : rewritableMethods.getEmulatedDispatchMethods()) {
+      // Dispatch interface.
+      DexType interfaceType = dispatchInterfaceTypeFor(appView, emulatedDispatchMethod);
+      DexEncodedMethod itfMethod =
+          generateInterfaceDispatchMethod(emulatedDispatchMethod, interfaceType);
+      synthesizeClassWithUniqueMethod(
+          builder,
+          itfAccessFlags,
+          interfaceType,
+          itfMethod,
+          "desugared library dispatch interface",
+          false);
+      // Dispatch holder.
+      DexType holderType = dispatchHolderTypeFor(appView, emulatedDispatchMethod);
+      DexEncodedMethod dispatchMethod =
+          generateHolderDispatchMethod(emulatedDispatchMethod, holderType, itfMethod.method);
+      synthesizeClassWithUniqueMethod(
+          builder,
+          holderAccessFlags,
+          holderType,
+          dispatchMethod,
+          "desugared library dispatch class",
+          false);
+    }
+  }
+
+  private DexProgramClass synthesizeClassWithUniqueMethod(
       Builder<?> builder,
       ClassAccessFlags accessFlags,
       DexType type,
       DexEncodedMethod uniqueMethod,
       String origin,
-      boolean addToMainDexList,
-      AppView<?> appView) {
-    DexItemFactory factory = appView.dexItemFactory();
+      boolean addToMainDexList) {
     DexProgramClass newClass =
         new DexProgramClass(
             type,
             null,
-            new SynthesizedOrigin(origin, BackportedMethodRewriter.class),
+            new SynthesizedOrigin(origin, getClass()),
             accessFlags,
             factory.objectType,
             DexTypeList.empty(),
@@ -272,64 +425,186 @@
                 ? DexEncodedMethod.EMPTY_ARRAY
                 : new DexEncodedMethod[] {uniqueMethod},
             factory.getSkipNameValidationForTesting(),
-            getChecksumSupplier(uniqueMethod, appView));
+            getChecksumSupplier(uniqueMethod));
     appView.appInfo().addSynthesizedClass(newClass);
     builder.addSynthesizedClass(newClass, addToMainDexList);
     return newClass;
   }
 
-  private static ChecksumSupplier getChecksumSupplier(DexEncodedMethod method, AppView<?> appView) {
+  private DexEncodedMethod generateInterfaceDispatchMethod(
+      DexMethod emulatedDispatchMethod, DexType interfaceType) {
+    MethodAccessFlags flags =
+        MethodAccessFlags.fromSharedAccessFlags(
+            Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
+    DexMethod newMethod =
+        factory.createMethod(
+            interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
+    return new DexEncodedMethod(
+        newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
+  }
+
+  private DexEncodedMethod generateHolderDispatchMethod(
+      DexMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
+    // The method should look like:
+    // static foo(rcvr, arg0, arg1) {
+    //    if (rcvr instanceof interfaceType) {
+    //      return invoke-interface receiver.foo(arg0, arg1);
+    //    } else {
+    //      return DesugarX.foo(rcvr, arg0, arg1)
+    //    }
+    // We do not deal with complex cases (multiple retargeting of the same signature in the
+    // same inheritance tree, etc., since they do not happen in the most common desugared library.
+    DexMethod desugarMethod =
+        appView
+            .options()
+            .desugaredLibraryConfiguration
+            .retargetMethod(emulatedDispatchMethod, appView);
+    assert desugarMethod != null; // This method is reached only for retarget core lib members.
+    DexMethod newMethod =
+        appView
+            .dexItemFactory()
+            .createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.name);
+    return DexEncodedMethod.toEmulateDispatchLibraryMethod(
+        emulatedDispatchMethod.holder,
+        newMethod,
+        desugarMethod,
+        itfMethod,
+        Collections.emptyList(),
+        appView);
+  }
+
+  private ChecksumSupplier getChecksumSupplier(DexEncodedMethod method) {
     if (!appView.options().encodeChecksums) {
       return DexProgramClass::invalidChecksumRequest;
     }
     return c -> method.method.hashCode();
   }
-  
+
+  public static DexType dispatchInterfaceTypeFor(AppView<?> appView, DexMethod method) {
+    return dispatchTypeFor(appView, method, "dispatchInterface");
+  }
+
+  static DexType dispatchHolderTypeFor(AppView<?> appView, DexMethod method) {
+    return dispatchTypeFor(appView, method, "dispatchHolder");
+  }
+
+  private static DexType dispatchTypeFor(AppView<?> appView, DexMethod method, String suffix) {
+    String prefix =
+        appView.options().desugaredLibraryConfiguration.getSynthesizedLibraryClassesPackagePrefix();
+    String descriptor =
+        "L"
+            + prefix
+            + UTILITY_CLASS_NAME_PREFIX
+            + '$'
+            + method.holder.getName()
+            + '$'
+            + method.name
+            + '$'
+            + suffix
+            + ';';
+    return appView.dexItemFactory().createType(descriptor);
+  }
+
   private MethodProvider getMethodProviderOrNull(DexMethod method) {
     DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
     assert original != null;
+    Map<DexType, DexType> backportCoreLibraryMembers =
+        appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
+    if (backportCoreLibraryMembers.containsKey(original.holder)) {
+      DexType newHolder = backportCoreLibraryMembers.get(original.holder);
+      DexMethod newMethod =
+          appView.dexItemFactory().createMethod(newHolder, original.proto, original.name);
+      MethodProvider provider = rewritableMethods.getProvider(newMethod);
+      if (provider != null) {
+        return provider;
+      }
+      RetargetCoreLibraryMethodProvider extraProvider =
+          new RetargetCoreLibraryMethodProvider(newHolder, original, true);
+      // TODO(b/139788786): cache this entry, but without writing into a lock free structure.
+      // rewritableMethods.addProvider(extraProvider);
+      return extraProvider;
+    }
     return rewritableMethods.getProvider(original);
   }
 
+  public static void checkForAssumedLibraryTypes(AppView<?> appView) {
+    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+        appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+    for (DexString methodName : retargetCoreLibMember.keySet()) {
+      for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+        DexClass typeClass = appView.definitionFor(inType);
+        if (typeClass == null) {
+          RewritableMethods.warnMissingRetargetCoreLibraryMember(inType, appView);
+        }
+      }
+    }
+  }
+
   private static final class RewritableMethods {
 
     // Map backported method to a provider for creating the actual target method (with code).
     private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>();
+    // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
+    // rewritten while the holder is non final but no superclass implement the method. In this case
+    // d8 needs to force resolution of given methods to see if the invoke needs to be rewritten.
+    private final Map<DexString, List<DexMethod>> virtualRewrites = new IdentityHashMap<>();
+    // non final virtual library methods requiring generation of emulated dispatch.
+    private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
 
     RewritableMethods(InternalOptions options, AppView<?> appView) {
-      if (!options.shouldBackportMethods()) {
-        return;
-      }
-      DexItemFactory factory = options.itemFactory;
-      if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
-        initializeAndroidKMethodProviders(factory);
-      }
-      if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
-        initializeAndroidNMethodProviders(factory);
-      }
-      if (options.minApiLevel < AndroidApiLevel.O.getLevel()) {
-        initializeAndroidOMethodProviders(factory);
+      if (options.shouldBackportMethods()) {
+        DexItemFactory factory = options.itemFactory;
+        if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
+          initializeAndroidKMethodProviders(factory);
+        }
+        if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
+          initializeAndroidNMethodProviders(factory);
+        }
+        if (options.minApiLevel < AndroidApiLevel.O.getLevel()) {
+          initializeAndroidOMethodProviders(factory);
+        }
+
+        // The following providers are currently not implemented at any API level in Android.
+        // They however require the Optional/Stream class to be present, either through desugared
+        // libraries or natively. If Optional/Stream class is not present, we do not desugar to
+        // avoid confusion in error messages.
+        if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
+            || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
+          initializeJava9OptionalMethodProviders(factory);
+          initializeJava10OptionalMethodProviders(factory);
+          initializeJava11OptionalMethodProviders(factory);
+        }
+        if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
+            || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
+          initializeStreamMethodProviders(factory);
+        }
+
+        // These are currently not implemented at any API level in Android.
+        initializeJava9MethodProviders(factory);
+        initializeJava10MethodProviders(factory);
+        initializeJava11MethodProviders(factory);
       }
 
-      // The following providers are currently not implemented at any API level in Android.
-      // They however require the Optional/Stream class to be present, either through desugared
-      // libraries or natively. If Optional/Stream class is not present, we do not desugar to
-      // avoid confusion in error messages.
-      if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
-          || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
-        initializeJava9OptionalMethodProviders(factory);
-        initializeJava10OptionalMethodProviders(factory);
-        initializeJava11OptionalMethodProviders(factory);
+      if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
+        initializeRetargetCoreLibraryMembers(appView);
       }
-      if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
-          || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
-        initializeStreamMethodProviders(factory);
-      }
+    }
 
-      // These are currently not implemented at any API level in Android.
-      initializeJava9MethodProviders(factory);
-      initializeJava10MethodProviders(factory);
-      initializeJava11MethodProviders(factory);
+    boolean matchesVirtualRewrite(DexMethod method) {
+      List<DexMethod> dexMethods = virtualRewrites.get(method.name);
+      if (dexMethods == null) {
+        return false;
+      }
+      for (DexMethod dexMethod : dexMethods) {
+        if (method.match(dexMethod)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    public Set<DexMethod> getEmulatedDispatchMethods() {
+      return emulatedDispatchMethods;
     }
 
     boolean isEmpty() {
@@ -394,9 +669,8 @@
 
       // int Objects.compare(T a, T b, Comparator<? super T> c)
       name = factory.createString("compare");
-      proto =
-          factory.createProto(
-              factory.intType, factory.objectType, factory.objectType, factory.comparatorType);
+      proto = factory.createProto(factory.intType, factory.objectType, factory.objectType,
+          factory.comparatorType);
       method = factory.createMethod(type, proto, name);
       addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_compare));
 
@@ -969,9 +1243,8 @@
 
       // String String.join(CharSequence, CharSequence...)
       name = factory.createString("join");
-      proto =
-          factory.createProto(
-              factory.stringType, factory.charSequenceType, factory.charSequenceArrayType);
+      proto = factory.createProto(factory.stringType, factory.charSequenceType,
+          factory.charSequenceArrayType);
       method = factory.createMethod(type, proto, name);
       addProvider(
           new MethodGenerator(method, BackportedMethods::StringMethods_joinArray, "joinArray"));
@@ -1006,13 +1279,19 @@
         name = factory.createString("multiplyFull");
         proto = factory.createProto(factory.longType, factory.intType, factory.intType);
         method = factory.createMethod(type, proto, name);
-        addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyFull));
+        addProvider(
+            new MethodGenerator(
+                method,
+                BackportedMethods::MathMethods_multiplyFull));
 
         // long {Math,StrictMath}.multiplyHigh(long, long)
         name = factory.createString("multiplyHigh");
         proto = factory.createProto(factory.longType, factory.longType, factory.longType);
         method = factory.createMethod(type, proto, name);
-        addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyHigh));
+        addProvider(
+            new MethodGenerator(
+                method,
+                BackportedMethods::MathMethods_multiplyHigh));
 
         // long {Math,StrictMath}.floorDiv(long, int)
         name = factory.createString("floorDiv");
@@ -1106,7 +1385,8 @@
       proto = factory.createProto(type, factory.objectArrayType);
       method = factory.createMethod(type, proto, name);
       addProvider(
-          new MethodGenerator(method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
+          new MethodGenerator(
+              method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
 
       // Set<E> Set.of(<args>) for 0 to 10 arguments and Set.of(E[])
       type = factory.setType;
@@ -1126,7 +1406,8 @@
       proto = factory.createProto(type, factory.objectArrayType);
       method = factory.createMethod(type, proto, name);
       addProvider(
-          new MethodGenerator(method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
+          new MethodGenerator(
+              method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
 
       // Map<K, V> Map.of(<K, V args>) for 0 to 10 pairs and Map.ofEntries(Map.Entry<K, V>[])
       type = factory.mapType;
@@ -1273,24 +1554,24 @@
       // Optional{void,Int,Long,Double}.stream()
       DexType[] optionalTypes =
           new DexType[] {
-            optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
+              optionalType,
+              factory.optionalDoubleType,
+              factory.optionalLongType,
+              factory.optionalIntType,
           };
       DexType[] streamReturnTypes =
           new DexType[] {
-            factory.streamType,
-            factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
-            factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
-            factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
+              factory.streamType,
+              factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
           };
       TemplateMethodFactory[] streamMethodFactories =
           new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_stream,
-            BackportedMethods::OptionalMethods_streamDouble,
-            BackportedMethods::OptionalMethods_streamLong,
-            BackportedMethods::OptionalMethods_streamInt,
+              BackportedMethods::OptionalMethods_stream,
+              BackportedMethods::OptionalMethods_streamDouble,
+              BackportedMethods::OptionalMethods_streamLong,
+              BackportedMethods::OptionalMethods_streamInt,
           };
       name = factory.createString("stream");
       for (int i = 0; i < optionalTypes.length; i++) {
@@ -1299,20 +1580,24 @@
         proto = factory.createProto(streamReturnType);
         method = factory.createMethod(optional, proto, name);
         addProvider(
-            new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional));
+            new StatifyingMethodGenerator(
+                method, streamMethodFactories[i], "stream", optional));
       }
 
       // Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
       DexType[] consumerTypes =
-          new DexType[] {
-            factory.consumerType, factory.doubleConsumer, factory.longConsumer, factory.intConsumer
+          new DexType[]{
+              factory.consumerType,
+              factory.doubleConsumer,
+              factory.longConsumer,
+              factory.intConsumer
           };
       TemplateMethodFactory[] methodFactories =
-          new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_ifPresentOrElse,
-            BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
-            BackportedMethods::OptionalMethods_ifPresentOrElseLong,
-            BackportedMethods::OptionalMethods_ifPresentOrElseInt
+          new TemplateMethodFactory[]{
+              BackportedMethods::OptionalMethods_ifPresentOrElse,
+              BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
+              BackportedMethods::OptionalMethods_ifPresentOrElseLong,
+              BackportedMethods::OptionalMethods_ifPresentOrElseInt
           };
       for (int i = 0; i < optionalTypes.length; i++) {
         DexType optional = optionalTypes[i];
@@ -1329,14 +1614,17 @@
       // Optional{void,Int,Long,Double}.orElseThrow()
       DexType[] optionalTypes =
           new DexType[] {
-            factory.optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
+              factory.optionalType,
+              factory.optionalDoubleType,
+              factory.optionalLongType,
+              factory.optionalIntType,
           };
       DexType[] returnTypes =
           new DexType[] {
-            factory.objectType, factory.doubleType, factory.longType, factory.intType,
+              factory.objectType,
+              factory.doubleType,
+              factory.longType,
+              factory.intType,
           };
       MethodInvokeRewriter[] rewriters =
           new MethodInvokeRewriter[] {
@@ -1357,17 +1645,17 @@
       // Optional{void,Int,Long,Double}.isEmpty()
       DexType[] optionalTypes =
           new DexType[] {
-            factory.optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
+              factory.optionalType,
+              factory.optionalDoubleType,
+              factory.optionalLongType,
+              factory.optionalIntType,
           };
       TemplateMethodFactory[] methodFactories =
-          new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_isEmpty,
-            BackportedMethods::OptionalMethods_isEmptyDouble,
-            BackportedMethods::OptionalMethods_isEmptyLong,
-            BackportedMethods::OptionalMethods_isEmptyInt
+          new TemplateMethodFactory[]{
+              BackportedMethods::OptionalMethods_isEmpty,
+              BackportedMethods::OptionalMethods_isEmptyDouble,
+              BackportedMethods::OptionalMethods_isEmptyLong,
+              BackportedMethods::OptionalMethods_isEmptyInt
           };
       DexString name = factory.createString("isEmpty");
       for (int i = 0; i < optionalTypes.length; i++) {
@@ -1388,7 +1676,76 @@
       DexProto proto = factory.createProto(factory.streamType, factory.objectType);
       DexMethod method = factory.createMethod(streamType, proto, name);
       addProvider(
-          new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
+          new MethodGenerator(
+              method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
+    }
+
+    private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
+      StringDiagnostic warning =
+          new StringDiagnostic(
+              "Cannot retarget core library member "
+                  + type.getName()
+                  + " because the class is missing.");
+      appView.options().reporter.warning(warning);
+    }
+
+    private void initializeRetargetCoreLibraryMembers(AppView<?> appView) {
+      assert appView.appInfo().hasClassHierarchy()
+          : "Class hierarchy required for desugared library.";
+      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+          appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+      for (DexString methodName : retargetCoreLibMember.keySet()) {
+        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+          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) {
+              if (!encodedMethod.isStatic()) {
+                virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
+                virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
+                if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+                  // In this case interface method rewriter takes care of it.
+                  continue;
+                } else if (!encodedMethod.isFinal()) {
+                  // Virtual rewrites require emulated dispatch for inheritance.
+                  // The call is rewritten to the dispatch holder class instead.
+                  handleEmulateDispatch(appView, encodedMethod.method);
+                  newHolder = dispatchHolderTypeFor(appView, encodedMethod.method);
+                }
+              }
+              DexProto proto = encodedMethod.method.proto;
+              DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
+              addProvider(
+                  new RetargetCoreLibraryMethodProvider(
+                      newHolder, method, encodedMethod.isStatic()));
+            }
+          }
+        }
+      }
+    }
+
+    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).";
+      return found;
+    }
+
+    private void handleEmulateDispatch(AppView<?> appView, DexMethod method) {
+      emulatedDispatchMethods.add(method);
+      if (!appView.options().isDesugaredLibraryCompilation()) {
+        // Add rewrite rules so keeps rules are correctly generated in the program.
+        DexType dispatchInterfaceType = dispatchInterfaceTypeFor(appView, method);
+        appView.rewritePrefix.rewriteType(dispatchInterfaceType, dispatchInterfaceType);
+        DexType dispatchHolderType = dispatchHolderTypeFor(appView, method);
+        appView.rewritePrefix.rewriteType(dispatchHolderType, dispatchHolderType);
+      }
     }
 
     private void addProvider(MethodProvider generator) {
@@ -1423,6 +1780,52 @@
     public abstract boolean requiresGenerationOfCode();
   }
 
+  private static class RetargetCoreLibraryMethodProvider extends MethodProvider {
+
+    private final DexType newHolder;
+    private DexMethod targetMethod;
+    private boolean isStatic;
+
+    RetargetCoreLibraryMethodProvider(DexType newHolder, DexMethod method, boolean isStatic) {
+      super(method);
+      this.newHolder = newHolder;
+      this.isStatic = isStatic;
+    }
+
+    @Override
+    public void rewriteInvoke(
+        InvokeMethod invoke,
+        InstructionListIterator iterator,
+        IRCode code,
+        AppView<?> appView,
+        Set<Value> affectedValues) {
+      iterator.replaceCurrentInstruction(
+          new InvokeStatic(provideMethod(appView), invoke.outValue(), invoke.inValues()));
+    }
+
+    @Override
+    public DexMethod provideMethod(AppView<?> appView) {
+      if (targetMethod != null) {
+        return targetMethod;
+      }
+      DexItemFactory factory = appView.dexItemFactory();
+      DexProto newProto =
+          isStatic ? method.proto : factory.prependTypeToProto(method.holder, method.proto);
+      targetMethod = factory.createMethod(newHolder, newProto, method.name);
+      return targetMethod;
+    }
+
+    @Override
+    public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
+      throw new Unreachable("Does not generate any method.");
+    }
+
+    @Override
+    public boolean requiresGenerationOfCode() {
+      return false;
+    }
+  }
+
   private static final class InvokeRewriter extends MethodProvider {
 
     private final MethodInvokeRewriter rewriter;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index baa6317..bdb4cd9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -110,6 +110,12 @@
     return libraryCompilation;
   }
 
+  public String getSynthesizedLibraryClassesPackagePrefix(AppView<?> appView) {
+    return appView.options().isDesugaredLibraryCompilation()
+        ? synthesizedLibraryClassesPackagePrefix
+        : "";
+  }
+
   public String getSynthesizedLibraryClassesPackagePrefix() {
     return synthesizedLibraryClassesPackagePrefix;
   }
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
deleted file mode 100644
index 3949767..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ /dev/null
@@ -1,441 +0,0 @@
-// 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.ir.desugar;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.*;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
-
-public class DesugaredLibraryRetargeter {
-
-  public static final String DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX =
-      "$r8$retargetLibraryMember$virtualDispatch";
-
-  private final AppView<AppInfoWithClassHierarchy> appView;
-  private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
-  // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
-  // rewritten while the holder is non final but no superclass implement the method. In this case
-  // d8 needs to force resolution of given methods to see if the invoke needs to be rewritten.
-  private final Map<DexString, List<DexMethod>> virtualRewrites = new IdentityHashMap<>();
-  // Non final virtual library methods requiring generation of emulated dispatch.
-  private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
-
-  public DesugaredLibraryRetargeter(AppView<?> appView) {
-    assert appView.appInfo().hasClassHierarchy()
-        : "Class hierarchy required for desugared library.";
-    this.appView = appView.withClassHierarchy();
-    if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
-      return;
-    }
-    new RetargetingSetup().setUpRetargeting();
-  }
-
-  public static void checkForAssumedLibraryTypes(AppView<?> appView) {
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
-    for (DexString methodName : retargetCoreLibMember.keySet()) {
-      for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-        DexClass typeClass = appView.definitionFor(inType);
-        if (typeClass == null) {
-          warnMissingRetargetCoreLibraryMember(inType, appView);
-        }
-      }
-    }
-  }
-
-  private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
-    StringDiagnostic warning =
-        new StringDiagnostic(
-            "Cannot retarget core library member "
-                + type.getName()
-                + " because the class is missing.");
-    appView.options().reporter.warning(warning);
-  }
-
-  // Used by the ListOfBackportedMethods utility.
-  void visit(Consumer<DexMethod> consumer) {
-    retargetLibraryMember.keySet().forEach(consumer);
-  }
-
-  public void desugar(IRCode code) {
-    if (retargetLibraryMember.isEmpty()) {
-      return;
-    }
-
-    InstructionListIterator iterator = code.instructionListIterator();
-    while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (!instruction.isInvokeMethod()) {
-        continue;
-      }
-
-      InvokeMethod invoke = instruction.asInvokeMethod();
-      DexMethod retarget = getRetargetLibraryMember(invoke.getInvokedMethod());
-      if (retarget == null) {
-        if (!matchesVirtualRewrite(invoke.getInvokedMethod())) {
-          continue;
-        }
-        // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
-        ResolutionResult resolutionResult =
-            appView
-                .appInfo()
-                .resolveMethod(invoke.getInvokedMethod().holder, invoke.getInvokedMethod());
-        if (resolutionResult.isFailedResolution()) {
-          continue;
-        }
-        DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
-        assert singleTarget != null;
-        retarget = getRetargetLibraryMember(singleTarget.method);
-        if (retarget == null) {
-          continue;
-        }
-      }
-
-      // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
-      // infinite loops. We do direct resolution. This is a very uncommon case.
-      if (invoke.isInvokeSuper() && matchesVirtualRewrite(invoke.getInvokedMethod())) {
-        DexEncodedMethod dexEncodedMethod =
-            appView
-                .appInfo()
-                .withClassHierarchy()
-                .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
-        // Final methods can be rewritten as a normal invoke.
-        if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
-          DexMethod retargetMethod =
-              appView
-                  .options()
-                  .desugaredLibraryConfiguration
-                  .retargetMethod(dexEncodedMethod.method, appView);
-          if (retargetMethod != null) {
-            iterator.replaceCurrentInstruction(
-                new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
-          }
-          continue;
-        }
-      }
-
-      iterator.replaceCurrentInstruction(
-          new InvokeStatic(retarget, invoke.outValue(), invoke.inValues()));
-    }
-  }
-
-  private DexMethod getRetargetLibraryMember(DexMethod method) {
-    Map<DexType, DexType> backportCoreLibraryMembers =
-        appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
-    if (backportCoreLibraryMembers.containsKey(method.holder)) {
-      DexType newHolder = backportCoreLibraryMembers.get(method.holder);
-      return appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
-    }
-    return retargetLibraryMember.get(method);
-  }
-
-  private boolean matchesVirtualRewrite(DexMethod method) {
-    List<DexMethod> dexMethods = virtualRewrites.get(method.name);
-    if (dexMethods == null) {
-      return false;
-    }
-    for (DexMethod dexMethod : dexMethods) {
-      if (method.match(dexMethod)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private class RetargetingSetup {
-
-    private void setUpRetargeting() {
-      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-          appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
-      for (DexString methodName : retargetCoreLibMember.keySet()) {
-        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-          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) {
-              if (!encodedMethod.isStatic()) {
-                virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
-                virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
-                if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
-                  // In this case interface method rewriter takes care of it.
-                  continue;
-                } else if (!encodedMethod.isFinal()) {
-                  // Virtual rewrites require emulated dispatch for inheritance.
-                  // The call is rewritten to the dispatch holder class instead.
-                  handleEmulateDispatch(appView, encodedMethod.method);
-                  newHolder = dispatchHolderTypeFor(encodedMethod.method);
-                }
-              }
-              DexProto proto = encodedMethod.method.proto;
-              DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
-              retargetLibraryMember.put(
-                  method, computeRetargetMethod(method, encodedMethod.isStatic(), newHolder));
-            }
-          }
-        }
-      }
-    }
-
-    private DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
-      DexItemFactory factory = appView.dexItemFactory();
-      DexProto newProto =
-          isStatic ? method.proto : factory.prependTypeToProto(method.holder, method.proto);
-      return factory.createMethod(newHolder, newProto, method.name);
-    }
-
-    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).";
-      return found;
-    }
-
-    private void handleEmulateDispatch(AppView<?> appView, DexMethod method) {
-      emulatedDispatchMethods.add(method);
-      if (!appView.options().isDesugaredLibraryCompilation()) {
-        // Add rewrite rules so keeps rules are correctly generated in the program.
-        DexType dispatchInterfaceType = dispatchInterfaceTypeFor(method);
-        appView.rewritePrefix.rewriteType(dispatchInterfaceType, dispatchInterfaceType);
-        DexType dispatchHolderType = dispatchHolderTypeFor(method);
-        appView.rewritePrefix.rewriteType(dispatchHolderType, dispatchHolderType);
-      }
-    }
-  }
-
-  public void synthesizeRetargetClasses(
-      DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
-      throws ExecutionException {
-    new EmulatedDispatchTreeFixer().fixApp(builder, executorService, converter);
-  }
-
-  // The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
-  // for inserting interfaces on library boundaries and forwarding methods in the program, and to
-  // synthesize the interfaces and emulated dispatch classes in the desugared library.
-  class EmulatedDispatchTreeFixer {
-
-    void fixApp(
-        DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
-        throws ExecutionException {
-      if (appView.options().isDesugaredLibraryCompilation()) {
-        synthesizeEmulatedDispatchMethods(builder);
-      } else {
-        addInterfacesAndForwardingMethods(executorService, converter);
-      }
-    }
-
-    private void addInterfacesAndForwardingMethods(
-        ExecutorService executorService, IRConverter converter) throws ExecutionException {
-      assert !appView.options().isDesugaredLibraryCompilation();
-      Map<DexType, List<DexMethod>> map = Maps.newIdentityHashMap();
-      for (DexMethod emulatedDispatchMethod : emulatedDispatchMethods) {
-        map.putIfAbsent(emulatedDispatchMethod.holder, new ArrayList<>(1));
-        map.get(emulatedDispatchMethod.holder).add(emulatedDispatchMethod);
-      }
-      List<DexEncodedMethod> addedMethods = new ArrayList<>();
-      for (DexProgramClass clazz : appView.appInfo().classes()) {
-        if (clazz.superType == null) {
-          assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
-          continue;
-        }
-        DexClass dexClass = 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)) {
-              addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
-            }
-          }
-        }
-      }
-      if (addedMethods.isEmpty()) {
-        return;
-      }
-      converter.processMethodsConcurrently(addedMethods, executorService);
-    }
-
-    private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) {
-      DexLibraryClass current = clazz;
-      while (current.type != appView.dexItemFactory().objectType) {
-        if (current.type == typeToInherit) {
-          return true;
-        }
-        current = appView.definitionFor(current.superType).asLibraryClass();
-      }
-      return false;
-    }
-
-    private List<DexEncodedMethod> addInterfacesAndForwardingMethods(
-        DexProgramClass clazz, List<DexMethod> dexMethods) {
-      // 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<DexEncodedMethod> newForwardingMethods = new ArrayList<>();
-      for (DexMethod dexMethod : dexMethods) {
-        DexType[] newInterfaces =
-            Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1);
-        newInterfaces[newInterfaces.length - 1] = dispatchInterfaceTypeFor(dexMethod);
-        clazz.interfaces = new DexTypeList(newInterfaces);
-        DexEncodedMethod dexEncodedMethod = clazz.lookupVirtualMethod(dexMethod);
-        if (dexEncodedMethod == null) {
-          DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz);
-          clazz.addVirtualMethod(newMethod);
-          newForwardingMethods.add(newMethod);
-        }
-      }
-      return newForwardingMethods;
-    }
-
-    private DexEncodedMethod createForwardingMethod(DexMethod 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.
-      // In desugared library, emulated interface methods can be overridden by retarget lib members.
-      DexMethod forwardMethod =
-          appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
-      assert forwardMethod != null && forwardMethod != target;
-      return DexEncodedMethod.createDesugaringForwardingMethod(
-          appView.definitionFor(target), clazz, forwardMethod, appView.dexItemFactory());
-    }
-
-    private void synthesizeEmulatedDispatchMethods(DexApplication.Builder<?> builder) {
-      assert appView.options().isDesugaredLibraryCompilation();
-      if (emulatedDispatchMethods.isEmpty()) {
-        return;
-      }
-      ClassAccessFlags itfAccessFlags =
-          ClassAccessFlags.fromSharedAccessFlags(
-              Constants.ACC_PUBLIC
-                  | Constants.ACC_SYNTHETIC
-                  | Constants.ACC_ABSTRACT
-                  | Constants.ACC_INTERFACE);
-      ClassAccessFlags holderAccessFlags =
-          ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-      for (DexMethod emulatedDispatchMethod : emulatedDispatchMethods) {
-        // Dispatch interface.
-        DexType interfaceType = dispatchInterfaceTypeFor(emulatedDispatchMethod);
-        DexEncodedMethod itfMethod =
-            generateInterfaceDispatchMethod(emulatedDispatchMethod, interfaceType);
-        BackportedMethodRewriter.synthesizeClassWithUniqueMethod(
-            builder,
-            itfAccessFlags,
-            interfaceType,
-            itfMethod,
-            "desugared library dispatch interface",
-            false,
-            appView);
-        // Dispatch holder.
-        DexType holderType = dispatchHolderTypeFor(emulatedDispatchMethod);
-        DexEncodedMethod dispatchMethod =
-            generateHolderDispatchMethod(emulatedDispatchMethod, holderType, itfMethod.method);
-        BackportedMethodRewriter.synthesizeClassWithUniqueMethod(
-            builder,
-            holderAccessFlags,
-            holderType,
-            dispatchMethod,
-            "desugared library dispatch class",
-            false,
-            appView);
-      }
-    }
-
-    private DexEncodedMethod generateInterfaceDispatchMethod(
-        DexMethod emulatedDispatchMethod, DexType interfaceType) {
-      MethodAccessFlags flags =
-          MethodAccessFlags.fromSharedAccessFlags(
-              Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
-      DexMethod newMethod =
-          appView
-              .dexItemFactory()
-              .createMethod(
-                  interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
-      return new DexEncodedMethod(
-          newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
-    }
-
-    private DexEncodedMethod generateHolderDispatchMethod(
-        DexMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
-      // The method should look like:
-      // static foo(rcvr, arg0, arg1) {
-      //    if (rcvr instanceof interfaceType) {
-      //      return invoke-interface receiver.foo(arg0, arg1);
-      //    } else {
-      //      return DesugarX.foo(rcvr, arg0, arg1)
-      //    }
-      // We do not deal with complex cases (multiple retargeting of the same signature in the
-      // same inheritance tree, etc., since they do not happen in the most common desugared library.
-      DexMethod desugarMethod =
-          appView
-              .options()
-              .desugaredLibraryConfiguration
-              .retargetMethod(emulatedDispatchMethod, appView);
-      assert desugarMethod != null; // This method is reached only for retarget core lib members.
-      DexMethod newMethod =
-          appView
-              .dexItemFactory()
-              .createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.name);
-      return DexEncodedMethod.toEmulateDispatchLibraryMethod(
-          emulatedDispatchMethod.holder,
-          newMethod,
-          desugarMethod,
-          itfMethod,
-          Collections.emptyList(),
-          appView);
-    }
-  }
-
-  private DexType dispatchInterfaceTypeFor(DexMethod method) {
-    return dispatchTypeFor(method, "dispatchInterface");
-  }
-
-  private DexType dispatchHolderTypeFor(DexMethod method) {
-    return dispatchTypeFor(method, "dispatchHolder");
-  }
-
-  private DexType dispatchTypeFor(DexMethod method, String suffix) {
-    String descriptor =
-        "L"
-            + appView
-                .options()
-                .desugaredLibraryConfiguration
-                .getSynthesizedLibraryClassesPackagePrefix()
-            + DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX
-            + '$'
-            + method.holder.getName()
-            + '$'
-            + method.name
-            + '$'
-            + suffix
-            + ';';
-    return appView.dexItemFactory().createType(descriptor);
-  }
-}