Reland "Split BackportedMethodRewriter and RetargetLibraryMember"

Bug: 151703213
Change-Id: I21bbd379a1bea012e17f4d16c364f6534df5a328
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 100b5aa..b843181 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.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 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()) {
-        BackportedMethodRewriter.checkForAssumedLibraryTypes(appView);
+        DesugaredLibraryRetargeter.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 4b8c6d4..01c5298 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -16,6 +16,7 @@
 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;
@@ -44,8 +45,7 @@
   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,11 +145,12 @@
   }
 
   @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);
     }
   }
 
@@ -272,6 +273,7 @@
         || 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);
   }
@@ -298,17 +300,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 + "'");
@@ -328,8 +330,11 @@
     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);
   }
 
@@ -364,8 +369,10 @@
 
   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);
   }
 
@@ -376,7 +383,8 @@
     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 4ac8995..4336f61 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,6 +45,7 @@
 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;
@@ -143,6 +144,7 @@
   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;
@@ -217,13 +219,16 @@
             .collect(Collectors.toList());
     if (options.isDesugaredLibraryCompilation()) {
       // Specific L8 Settings.
-      // BackportedMethodRewriter is needed for retarget core library members and backports.
+      // DesugaredLibraryRetargeter 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.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
+      this.desugaredLibraryRetargeter =
+          options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+              ? null
+              : new DesugaredLibraryRetargeter(appView);
       this.interfaceMethodRewriter =
           options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
               ? null
@@ -231,6 +236,10 @@
       this.lambdaRewriter = new LambdaRewriter(appView);
       this.desugaredLibraryAPIConverter =
           new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
+      this.backportedMethodRewriter =
+          options.testing.forceLibBackportsInL8CfToCf
+              ? new BackportedMethodRewriter(appView, this)
+              : null;
       this.twrCloseResourceRewriter = null;
       this.lambdaMerger = null;
       this.covariantReturnTypeAnnotationTransformer = null;
@@ -268,6 +277,10 @@
             ? 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())
@@ -450,7 +463,16 @@
 
   private void synthesizeJava8UtilityClass(
       Builder<?> builder, ExecutorService executorService) throws ExecutionException {
-    backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
+    if (backportedMethodRewriter != null) {
+      backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
+    }
+  }
+
+  private void synthesizeRetargetClass(Builder<?> builder, ExecutorService executorService)
+      throws ExecutionException {
+    if (desugaredLibraryRetargeter != null) {
+      desugaredLibraryRetargeter.synthesizeRetargetClasses(builder, executorService, this);
+    }
   }
 
   private void synthesizeEnumUnboxingUtilityClass(
@@ -482,6 +504,8 @@
     desugarInterfaceMethods(builder, ExcludeDexResources, executor);
     synthesizeTwrCloseResourceUtilityClass(builder, executor);
     synthesizeJava8UtilityClass(builder, executor);
+    synthesizeRetargetClass(builder, executor);
+
     processCovariantReturnTypeAnnotations(builder);
     generateDesugaredLibraryAPIWrappers(builder, executor);
 
@@ -747,6 +771,7 @@
     printPhase("Utility classes synthesis");
     synthesizeTwrCloseResourceUtilityClass(builder, executorService);
     synthesizeJava8UtilityClass(builder, executorService);
+    synthesizeRetargetClass(builder, executorService);
     handleSynthesizedClassMapping(builder);
     synthesizeEnumUnboxingUtilityClass(builder, executorService);
 
@@ -1370,9 +1395,20 @@
       codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
       timing.end();
     }
-    timing.begin("Rewrite backport methods");
-    backportedMethodRewriter.desugar(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("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 962170d..43db91d 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,7 +21,6 @@
 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;
@@ -31,7 +30,6 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.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;
@@ -54,13 +52,10 @@
 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;
@@ -113,15 +108,17 @@
       if (androidApp != null) {
         DexApplication app =
             new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
-        appInfo =
-            options.desugaredLibraryConfiguration.getRewritePrefix().isEmpty()
-                ? new AppInfo(app)
-                : new AppInfoWithClassHierarchy(app);
+        appInfo = new AppInfoWithClassHierarchy(app);
       }
       AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
       BackportedMethodRewriter.RewritableMethods rewritableMethods =
           new BackportedMethodRewriter.RewritableMethods(options, appView);
       rewritableMethods.visit(methods::add);
+      if (appInfo != null) {
+        DesugaredLibraryRetargeter desugaredLibraryRetargeter =
+            new DesugaredLibraryRetargeter(appView);
+        desugaredLibraryRetargeter.visit(methods::add);
+      }
       return methods;
     } catch (ExecutionException e) {
       throw unwrapExecutionException(e);
@@ -146,49 +143,10 @@
           && !invoke.isInvokeStatic()) {
         continue;
       }
+
       MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod());
       if (provider == null) {
-        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;
-        }
+        continue;
       }
 
       provider.rewriteInvoke(invoke, iterator, code, appView, affectedValues);
@@ -222,11 +180,6 @@
     if (!enabled) {
       return;
     }
-    if (appView.options().isDesugaredLibraryCompilation()) {
-      synthesizeEmulatedDispatchMethods(builder);
-    } else {
-      addInterfacesAndForwardingMethods(executorService);
-    }
     if (holders.isEmpty()) {
       return;
     }
@@ -243,8 +196,9 @@
       }
     }
 
-    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
@@ -262,7 +216,12 @@
       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));
@@ -273,140 +232,27 @@
               method.holder,
               dexEncodedMethod,
               "java8 methods utility class",
-              addToMainDexList);
+              addToMainDexList,
+              appView);
       // The following may add elements to methodsProviders.
       converter.optimizeSynthesizedClass(utilityClass, executorService);
     }
   }
 
-  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(
+  static DexProgramClass synthesizeClassWithUniqueMethod(
       Builder<?> builder,
       ClassAccessFlags accessFlags,
       DexType type,
       DexEncodedMethod uniqueMethod,
       String origin,
-      boolean addToMainDexList) {
+      boolean addToMainDexList,
+      AppView<?> appView) {
+    DexItemFactory factory = appView.dexItemFactory();
     DexProgramClass newClass =
         new DexProgramClass(
             type,
             null,
-            new SynthesizedOrigin(origin, getClass()),
+            new SynthesizedOrigin(origin, BackportedMethodRewriter.class),
             accessFlags,
             factory.objectType,
             DexTypeList.empty(),
@@ -425,165 +271,31 @@
                 ? DexEncodedMethod.EMPTY_ARRAY
                 : new DexEncodedMethod[] {uniqueMethod},
             factory.getSkipNameValidationForTesting(),
-            getChecksumSupplier(uniqueMethod));
+            getChecksumSupplier(uniqueMethod, appView));
     appView.appInfo().addSynthesizedClass(newClass);
     builder.addSynthesizedClass(newClass, addToMainDexList);
     return newClass;
   }
 
-  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) {
+  private static ChecksumSupplier getChecksumSupplier(DexEncodedMethod method, AppView<?> appView) {
     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()) {
-        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);
-      }
 
       if (options.testing.forceLibBackportsInL8CfToCf) {
         DexItemFactory factory = options.itemFactory;
@@ -591,28 +303,44 @@
         initializeJava10OptionalMethodProviders(factory);
         initializeJava11OptionalMethodProviders(factory);
         initializeStreamMethodProviders(factory);
+        return;
       }
 
-      if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
-        initializeRetargetCoreLibraryMembers(appView);
+      if (!options.shouldBackportMethods()) {
+        return;
       }
-    }
 
-    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;
-    }
+      DexItemFactory factory = options.itemFactory;
 
-    public Set<DexMethod> getEmulatedDispatchMethods() {
-      return emulatedDispatchMethods;
+      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);
     }
 
     boolean isEmpty() {
@@ -677,8 +405,9 @@
 
       // 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));
 
@@ -1251,8 +980,9 @@
 
       // 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"));
@@ -1287,19 +1017,13 @@
         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");
@@ -1393,8 +1117,7 @@
       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;
@@ -1414,8 +1137,7 @@
       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;
@@ -1562,24 +1284,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++) {
@@ -1588,24 +1310,20 @@
         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];
@@ -1622,17 +1340,14 @@
       // 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[] {
@@ -1653,17 +1368,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++) {
@@ -1684,76 +1399,7 @@
       DexProto proto = factory.createProto(factory.streamType, factory.objectType);
       DexMethod method = factory.createMethod(streamType, proto, name);
       addProvider(
-          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);
-      }
+          new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
     }
 
     private void addProvider(MethodProvider generator) {
@@ -1788,52 +1434,6 @@
     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 bdb4cd9..baa6317 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,12 +110,6 @@
     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
new file mode 100644
index 0000000..3949767
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -0,0 +1,441 @@
+// 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);
+  }
+}