Revert "Reland "Enabling machine desugared library specification""

This reverts commit 0a497e239595a917d0630f683001bc542a787d81.


Revert "Use machine desugared library flags"

This reverts commit 753ea6073fc6d8d6a0979cbb0a6331425237ca8d.


Revert "Machine flags for interface method desugaring"

This reverts commit f1354dadcd044c16344359d9b43266629cc5ad2d.


Revert "Use machine desugared library conf when possible"

This reverts commit 7ee13eb7141cb3231fb49f097c0a527f5cb45396.


Revert "Use more machine desugared library flags"

This reverts commit 8b6f2291df515a94427acd4dd422756d8bf0f522.


Revert "Stub retargeter"

This reverts commit e83d09622b71ae8183106c925c997a364ed71c4d.


Revert "Fix cast"

This reverts commit d6732348f6370ca84e2f859df087f1b083d0792f.


Revert "Revert "Stub retargeter""

This reverts commit 4cb874265aec9256c3f9cb4b301df4e87b14f5bf.


Revert "Fix JDK11 desugared library build"

This reverts commit 9abafda3f4d28815d2e71f3f93d24be8c390b335.

Reason for revert: Blocking google3 roll
Change-Id: I4cfd412cfbce77aa810b77b2ac596c8ab250528e
diff --git a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
index f3a4daa..565ffd9 100644
--- a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "chm_only_desugar_jdk_libs",
   "version": "1.0.12",
-  "required_compilation_api_level": 30,
+  "required_compilation_api_level": 31,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index b075745..fb03dbb 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
   "version": "2.0.0",
-  "required_compilation_api_level": 30,
+  "required_compilation_api_level": 31,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
   "common_flags": [
@@ -205,7 +205,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index 1482909..fac2ec0 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs_alternative_3",
   "version": "2.0.0",
-  "required_compilation_api_level": 30,
+  "required_compilation_api_level": 31,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
@@ -209,7 +209,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index 7ef1569..bce73f0 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -105,7 +105,7 @@
   InternalOptions getInternalOptions() {
     InternalOptions options = new InternalOptions(factory, getReporter());
     options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
-    options.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+    options.desugaredLibrarySpecification = desugaredLibrarySpecification;
     return options;
   }
 
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 0967f55..8dd34a8 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -498,7 +498,8 @@
     internal.encodeChecksums = getIncludeClassesChecksum();
     internal.dexClassChecksumFilter = getDexClassChecksumFilter();
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
-    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+
+    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 6da92e2..26c99ce 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -194,7 +194,7 @@
     internal.enableInheritanceClassInDexDistributor = false;
 
     assert desugaredLibrarySpecification != null;
-    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
     internal.synthesizedClassPrefix =
         desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix();
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 85c05f6..194c531 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -311,7 +311,7 @@
       if (!options.mainDexKeepRules.isEmpty()) {
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
-      if (options.machineDesugaredLibrarySpecification.hasRetargeting()) {
+      if (!options.desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()) {
         DesugaredLibraryRetargeterLibraryTypeSynthesizer.checkForAssumedLibraryTypes(appView);
         DesugaredLibraryRetargeterLibraryTypeSynthesizer.amendLibraryWithRetargetedMembers(appView);
       }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5bc9ddd..bf04680 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -943,7 +943,7 @@
 
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
-    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index cce0852..2e2aa7a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.google.common.collect.ImmutableList;
@@ -17,6 +18,7 @@
 
 public class AndroidApiReferenceLevelCache {
 
+  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
   private final AndroidApiLevelCompute apiLevelCompute;
   private final AndroidApiLevelDatabase androidApiLevelDatabase;
   private final AppView<?> appView;
@@ -31,6 +33,7 @@
     factory = appView.dexItemFactory();
     androidApiLevelDatabase =
         new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
+    desugaredLibrarySpecification = appView.options().desugaredLibrarySpecification;
   }
 
   public static AndroidApiReferenceLevelCache create(
@@ -72,7 +75,7 @@
     if (reference.getContextType() == factory.objectType) {
       return appView.computedMinApiLevel();
     }
-    if (appView.options().machineDesugaredLibrarySpecification.isSupported(reference)) {
+    if (desugaredLibrarySpecification.isSupported(reference, appView)) {
       // If we end up desugaring the reference, the library classes is bridged by j$ which is part
       // of the program.
       return appView.computedMinApiLevel();
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index ef86f73..f9b13a4 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowExceptionCode;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -111,10 +112,12 @@
       new ConcurrentHashMap<>();
   private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
   private final AndroidApiLevelCompute apiLevelCompute;
+  private final LegacyDesugaredLibrarySpecification desugaredLibraryConfiguration;
 
   public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
     apiLevelCompute = appView.apiLevelCompute();
+    desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification;
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
@@ -217,10 +220,7 @@
         || libraryClass.getType().toDescriptorString().startsWith("Ljava/")) {
       return;
     }
-    if (appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .isSupported(libraryClass.getType())) {
+    if (desugaredLibraryConfiguration.isSupported(libraryClass.getType(), appView)) {
       return;
     }
     appView
diff --git a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
index eaec5a0..a720021 100644
--- a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
+++ b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.references.ArrayReference;
 import com.android.tools.r8.references.ClassReference;
@@ -32,11 +32,13 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.NopDiagnosticsHandler;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.TypeReferenceUtils;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
@@ -69,7 +71,7 @@
       return false;
     }
     return namingLens.hasPrefixRewritingLogic()
-        || options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
+        || options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces();
   }
 
   private void run() {
@@ -78,15 +80,18 @@
   }
 
   private Predicate<DexType> createTargetPredicate() {
-    MachineDesugaredLibrarySpecification desugaredLibrarySpecification =
-        options.machineDesugaredLibrarySpecification;
+    LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+        options.desugaredLibrarySpecification;
+    Set<DexType> potentialTypesToKeep =
+        SetUtils.newIdentityHashSet(
+            desugaredLibrarySpecification.getCustomConversions().values(),
+            desugaredLibrarySpecification.getEmulateLibraryInterface().values());
     byte[] synthesizedLibraryClassesPackageDescriptorPrefix =
         DexString.encodeToMutf8(
             "L" + desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
     return type ->
         namingLens.prefixRewrittenType(type) != null
-            || desugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
-            || desugaredLibrarySpecification.isCustomConversionRewrittenType(type)
+            || potentialTypesToKeep.contains(type)
             || type.getDescriptor().startsWith(synthesizedLibraryClassesPackageDescriptorPrefix);
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 3e33ca8..cc05c31 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -25,7 +25,7 @@
 
   static CodeToKeep createCodeToKeep(InternalOptions options, NamingLens namingLens) {
     if ((!namingLens.hasPrefixRewritingLogic()
-            && !options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces())
+            && !options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces())
         || options.isDesugaredLibraryCompilation()
         || options.testing.enableExperimentalDesugaredLibraryKeepRuleGenerator) {
       return new NopCodeToKeep();
@@ -57,23 +57,27 @@
     }
 
     private final NamingLens namingLens;
+    private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
     private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
     private final InternalOptions options;
 
     public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
       this.namingLens = namingLens;
       this.options = options;
+      potentialTypesToKeep.addAll(
+          options.desugaredLibrarySpecification.getEmulateLibraryInterface().values());
+      potentialTypesToKeep.addAll(
+          options.desugaredLibrarySpecification.getCustomConversions().values());
     }
 
     private boolean shouldKeep(DexType type) {
       return namingLens.prefixRewrittenType(type) != null
-          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
-          || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
+          || potentialTypesToKeep.contains(type)
           // TODO(b/158632510): This should prefix match on DexString.
           || type.toDescriptorString()
               .startsWith(
                   "L"
-                      + options.machineDesugaredLibrarySpecification
+                      + options.desugaredLibrarySpecification
                           .getSynthesizedLibraryClassesPackagePrefix());
     }
 
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 11d7e80..f99e380 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
@@ -128,16 +128,24 @@
     DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
     assert original != null;
     MethodProvider provider = rewritableMethods.getProvider(original);
-    // Old versions of desugared library have in the jar file pre-desugared code. This is used
-    // to undesugar pre-desugared code, then the code is re-desugared with D8/R8. This is
-    // maintained for legacy only, recent desugared library should not be shipped with
-    // pre-desugared code.
-    Map<DexType, DexType> legacyBackport =
-        appView.options().machineDesugaredLibrarySpecification.getLegacyBackport();
+    // TODO(b/150693139): Since the DesugarLibraryRetargeter is run during IR processing while the
+    // backported method rewriter is run in cf to cf, we need here to compute if the method is
+    // actually going to be retargeted through desugared library backports, and compute the
+    // corresponding backported method if so. This can be removed once the DesugarLibraryRetargeter
+    // has been moved as a cf to cf transformation.
     if (provider == null
         && appView.options().isDesugaredLibraryCompilation()
-        && legacyBackport.containsKey(method.holder)) {
-      DexType newHolder = legacyBackport.get(method.holder);
+        && appView
+            .options()
+            .desugaredLibrarySpecification
+            .getBackportCoreLibraryMember()
+            .containsKey(method.holder)) {
+      DexType newHolder =
+          appView
+              .options()
+              .desugaredLibrarySpecification
+              .getBackportCoreLibraryMember()
+              .get(method.holder);
       DexMethod backportedMethod =
           appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
       provider = rewritableMethods.getProvider(backportedMethod);
@@ -1437,7 +1445,7 @@
     }
 
     private void addProvider(MethodProvider generator) {
-      if (appView.options().machineDesugaredLibrarySpecification.isSupported(generator.method)) {
+      if (appView.options().desugaredLibrarySpecification.isSupported(generator.method, appView)) {
         // TODO(b/174453232): Remove this after the configuration file format has bee updated
         // with the "rewrite_method" section.
         if (generator.method.getHolderType() == appView.dexItemFactory().objectsType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 1243435..1fd0ad0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -57,7 +57,7 @@
         InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
         RetargetingInfo retargetingInfo) {
       ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
-      if (appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
+      if (!appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
           && !appView.options().isDesugaredLibraryCompilation()) {
         desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo));
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index e55c246..33c69ac 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -82,9 +82,9 @@
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = null;
     desugaredLibraryRetargeter =
-        appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
-            ? new DesugaredLibraryRetargeter(appView)
-            : null;
+        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
+            ? null
+            : new DesugaredLibraryRetargeter(appView);
     if (desugaredLibraryRetargeter != null) {
       desugarings.add(desugaredLibraryRetargeter);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 90f2505..bf96b67 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -4,11 +4,20 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
@@ -22,7 +31,7 @@
 
   public abstract DexType rewrittenType(DexType type, AppView<?> appView);
 
-  public abstract DexType rewrittenContextType(DexType type);
+  public abstract DexType rewrittenContextType(DexType type, AppView<?> appView);
 
   public boolean hasRewrittenType(DexType type, AppView<?> appView) {
     return rewrittenType(type, appView) != null;
@@ -44,34 +53,195 @@
 
   public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
 
-  public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
+  public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
 
-    private final Map<DexType, DexType> rewriteType;
-    private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+    private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
+    private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
+    private final Map<DexString, DexString> initialPrefixes;
+    private final DexItemFactory factory;
+    private final boolean l8Compilation;
 
-    public MachineDesugarPrefixRewritingMapper(MachineDesugaredLibrarySpecification specification) {
-      this.rewriteType = new ConcurrentHashMap<>(specification.getRewriteType());
-      rewriteDerivedTypeOnly = specification.getRewriteDerivedTypeOnly();
+    public DesugarPrefixRewritingMapper(
+        Map<String, String> prefixes, DexItemFactory itemFactory, boolean libraryCompilation) {
+      assert itemFactory != null || prefixes.isEmpty();
+      this.factory = itemFactory;
+      this.l8Compilation = libraryCompilation;
+      ImmutableMap.Builder<DexString, DexString> builder = ImmutableMap.builder();
+      for (String key : prefixes.keySet()) {
+        builder.put(toDescriptorPrefix(key), toDescriptorPrefix(prefixes.get(key)));
+      }
+      this.initialPrefixes = builder.build();
+      validatePrefixes(prefixes);
+    }
+
+    private DexString toDescriptorPrefix(String prefix) {
+      return factory.createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
+    }
+
+    @Override
+    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
+      rewritten.keySet().forEach(consumer);
+    }
+
+    private void validatePrefixes(Map<String, String> initialPrefixes) {
+      String[] prefixes = initialPrefixes.keySet().toArray(new String[0]);
+      for (int i = 0; i < prefixes.length; i++) {
+        for (int j = i + 1; j < prefixes.length; j++) {
+          String small, large;
+          if (prefixes[i].length() < prefixes[j].length()) {
+            small = prefixes[i];
+            large = prefixes[j];
+          } else {
+            small = prefixes[j];
+            large = prefixes[i];
+          }
+          if (large.startsWith(small)) {
+            throw new CompilationError(
+                "Inconsistent prefix in desugared library:"
+                    + " Should a class starting with "
+                    + small
+                    + " be rewritten using "
+                    + small
+                    + " -> "
+                    + initialPrefixes.get(small)
+                    + " or using "
+                    + large
+                    + " - > "
+                    + initialPrefixes.get(large)
+                    + " ?");
+          }
+        }
+      }
     }
 
     @Override
     public DexType rewrittenType(DexType type, AppView<?> appView) {
-      if (type.isArrayType()) {
-        DexType rewrittenBaseType =
-            rewrittenType(type.toBaseType(appView.dexItemFactory()), appView);
-        if (rewrittenBaseType == null) {
-          return null;
-        }
-        return appView
-            .dexItemFactory()
-            .createArrayType(type.getNumberOfLeadingSquareBrackets(), rewrittenBaseType);
+      assert appView != null || l8Compilation;
+      if (notRewritten.contains(type)) {
+        return null;
       }
+      if (rewritten.containsKey(type)) {
+        return rewritten.get(type);
+      }
+      return computePrefix(type, appView);
+    }
+
+    @Override
+    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
+      DexType rewritten = rewrittenType(type, appView);
+      if (rewritten != null) {
+        return rewritten;
+      }
+      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+          appView.options().desugaredLibrarySpecification;
+      if (desugaredLibrarySpecification.getEmulateLibraryInterface().containsKey(type)) {
+        return desugaredLibrarySpecification.getEmulateLibraryInterface().get(type);
+      }
+      for (Map<DexType, DexType> value :
+          desugaredLibrarySpecification.getRetargetCoreLibMember().values()) {
+        if (value.containsKey(type)) {
+          // Hack until machine specification are ready.
+          String prefix =
+              DescriptorUtils.getJavaTypeFromBinaryName(
+                  desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
+          String interfaceType = type.toString();
+          int firstPackage = interfaceType.indexOf('.');
+          return appView
+              .dexItemFactory()
+              .createType(
+                  DescriptorUtils.javaTypeToDescriptor(
+                      prefix + interfaceType.substring(firstPackage + 1)));
+        }
+      }
+      return null;
+    }
+
+    // Besides L8 compilation, program types should not be rewritten.
+    private void failIfRewritingProgramType(DexType type, AppView<?> appView) {
+      if (l8Compilation) {
+        return;
+      }
+
+      DexType dexType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
+      DexClass dexClass = appView.definitionFor(dexType);
+      if (dexClass != null && dexClass.isProgramClass()) {
+        appView
+            .options()
+            .reporter
+            .error(
+                "Cannot compile program class "
+                    + dexType
+                    + " since it conflicts with a desugared library rewriting rule.");
+      }
+    }
+
+    @Override
+    public void rewriteType(DexType type, DexType rewrittenType) {
+      assert !notRewritten.contains(type)
+          : "New rewriting rule for "
+              + type
+              + " but the compiler has already made decisions based on the fact that this type was"
+              + " not rewritten";
+      assert !rewritten.containsKey(type) || rewritten.get(type) == rewrittenType
+          : "New rewriting rule for "
+              + type
+              + " but the compiler has already made decisions based on a different rewriting rule"
+              + " for this type";
+      rewritten.put(type, rewrittenType);
+    }
+
+    private DexType computePrefix(DexType type, AppView<?> appView) {
+      DexString prefixToMatch = type.descriptor.withoutArray(factory);
+      DexType result = lookup(type, prefixToMatch, initialPrefixes);
+      if (result != null) {
+        failIfRewritingProgramType(type, appView);
+        return result;
+      }
+      notRewritten.add(type);
+      return null;
+    }
+
+    private DexType lookup(DexType type, DexString prefixToMatch, Map<DexString, DexString> map) {
+      // TODO(b/154800164): We could use tries instead of looking-up everywhere.
+      for (DexString prefix : map.keySet()) {
+        if (prefixToMatch.startsWith(prefix)) {
+          DexString rewrittenTypeDescriptor =
+              type.descriptor.withNewPrefix(prefix, map.get(prefix), factory);
+          DexType rewrittenType = factory.createType(rewrittenTypeDescriptor);
+          rewriteType(type, rewrittenType);
+          return rewrittenType;
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public boolean isRewriting() {
+      return true;
+    }
+  }
+
+  public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
+
+    private final PrefixRewritingMapper mapper;
+    private final Map<DexType, DexType> rewriteType;
+    private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
+    public MachineDesugarPrefixRewritingMapper(
+        PrefixRewritingMapper mapper, MachineRewritingFlags flags) {
+      this.mapper = mapper;
+      this.rewriteType = new ConcurrentHashMap<>(flags.getRewriteType());
+      rewriteDerivedTypeOnly = flags.getRewriteDerivedTypeOnly();
+    }
+
+    @Override
+    public DexType rewrittenType(DexType type, AppView<?> appView) {
+      assert mapper.rewrittenType(type, appView) == rewriteType.get(type);
       return rewriteType.get(type);
     }
 
     @Override
-    public DexType rewrittenContextType(DexType context) {
-      assert !context.isArrayType();
+    public DexType rewrittenContextType(DexType context, AppView<?> appView) {
       if (rewriteType.containsKey(context)) {
         return rewriteType.get(context);
       }
@@ -80,6 +250,7 @@
 
     @Override
     public void rewriteType(DexType type, DexType rewrittenType) {
+      mapper.rewriteType(type, rewrittenType);
       rewriteType.compute(
           type,
           (t, val) -> {
@@ -107,7 +278,7 @@
     }
 
     @Override
-    public DexType rewrittenContextType(DexType type) {
+    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
       return null;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index b66a96a..150e6c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -20,13 +20,13 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APICallbackWrapperCfCodeProvider;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -109,8 +109,8 @@
     if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
         || appView
             .options()
-            .machineDesugaredLibrarySpecification
-            .getEmulatedInterfaces()
+            .desugaredLibrarySpecification
+            .getEmulateLibraryInterface()
             .containsKey(method.getHolderType())) {
       return false;
     }
@@ -127,7 +127,7 @@
         return false;
       }
     }
-    if (!appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()
+    if (!appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()
         && appView.options().isDesugaredLibraryCompilation()) {
       return false;
     }
@@ -178,13 +178,13 @@
   }
 
   private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
-    if (appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
+    if (appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
       return true;
     }
-    MachineDesugaredLibrarySpecification specification =
-        appView.options().machineDesugaredLibrarySpecification;
-    return !(specification.getEmulatedInterfaces().containsKey(dexClass.type)
-        || specification.isEmulatedInterfaceRewrittenType(dexClass.type));
+    Map<DexType, DexType> emulateLibraryInterfaces =
+        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
+    return !(emulateLibraryInterfaces.containsKey(dexClass.type)
+        || emulateLibraryInterfaces.containsValue(dexClass.type));
   }
 
   private ProgramMethod generateCallbackMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index f234a1b..987dc8d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -181,8 +181,8 @@
     return interfaceResult != null
         && appView
             .options()
-            .machineDesugaredLibrarySpecification
-            .getEmulatedInterfaces()
+            .desugaredLibrarySpecification
+            .getEmulateLibraryInterface()
             .containsKey(interfaceResult.getHolderType());
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 71442ac..4a8cabe 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -13,14 +13,12 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -40,7 +38,6 @@
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
@@ -177,21 +174,17 @@
   private DexMethod getCustomConversion(DexType type, DexType srcType, DexType destType) {
     // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
     // But everything is going to be rewritten, so we need to use vivifiedType and type".
-    Pair<DexType, DexString> pair =
-        appView.options().machineDesugaredLibrarySpecification.getCustomConversions().get(type);
-    if (pair != null) {
+    DexType conversionHolder =
+        appView.options().desugaredLibrarySpecification.getCustomConversions().get(type);
+    if (conversionHolder != null) {
       return factory.createMethod(
-          pair.getFirst(), factory.createProto(destType, srcType), pair.getSecond());
+          conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
     }
     return null;
   }
 
   private boolean canConvert(DexType type) {
-    return appView
-            .options()
-            .machineDesugaredLibrarySpecification
-            .getCustomConversions()
-            .containsKey(type)
+    return appView.options().desugaredLibrarySpecification.getCustomConversions().containsKey(type)
         || canGenerateWrapper(type);
   }
 
@@ -219,7 +212,7 @@
   }
 
   private boolean canGenerateWrapper(DexType type) {
-    return appView.options().machineDesugaredLibrarySpecification.getWrappers().containsKey(type);
+    return appView.options().desugaredLibrarySpecification.getWrapperConversions().contains(type);
   }
 
   private DexClass getValidClassToWrap(DexType type) {
@@ -465,8 +458,9 @@
       if (holderClass == null) {
         assert appView
             .options()
-            .machineDesugaredLibrarySpecification
-            .isEmulatedInterfaceRewrittenType(method.getHolderType());
+            .desugaredLibrarySpecification
+            .getEmulateLibraryInterface()
+            .containsValue(method.getHolderType());
         isInterface = true;
       } else {
         isInterface = holderClass.isInterface();
@@ -547,17 +541,19 @@
   }
 
   private Iterable<DexMethod> allImplementedMethods(DexClass clazz) {
-    if (appView.options().machineDesugaredLibrarySpecification != null) {
+    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
       return appView
           .options()
+          .testing
           .machineDesugaredLibrarySpecification
+          .getRewritingFlags()
           .getWrappers()
           .get(clazz.type);
     }
     List<DexEncodedMethod> dexEncodedMethods =
         allImplementedMethodsCache.computeIfAbsent(
             clazz.type, type -> internalAllImplementedMethods(clazz));
-    return Iterables.transform(dexEncodedMethods, DexEncodedMember::getReference);
+    return Iterables.transform(dexEncodedMethods, m -> m.getReference());
   }
 
   private List<DexEncodedMethod> internalAllImplementedMethods(DexClass libraryClass) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 1e871b3..6a6a2d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -6,9 +6,14 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -18,20 +23,56 @@
   private final boolean libraryCompilation;
   private final HumanTopLevelFlags topLevelFlags;
   private final HumanRewritingFlags rewritingFlags;
+  private final PrefixRewritingMapper prefixRewritingMapper;
+
+  public static HumanDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
+      Map<String, String> prefix, InternalOptions options) {
+    return new HumanDesugaredLibrarySpecification(
+        HumanTopLevelFlags.empty(),
+        HumanRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
+        true,
+        options.itemFactory);
+  }
+
+  public static HumanDesugaredLibrarySpecification empty() {
+    return new HumanDesugaredLibrarySpecification(
+        HumanTopLevelFlags.empty(), HumanRewritingFlags.empty(), false, null) {
+
+      @Override
+      public boolean isSupported(DexReference reference, AppView<?> appView) {
+        return false;
+      }
+
+      @Override
+      public boolean isEmptyConfiguration() {
+        return true;
+      }
+    };
+  }
 
   public HumanDesugaredLibrarySpecification(
       HumanTopLevelFlags topLevelFlags,
       HumanRewritingFlags rewritingFlags,
-      boolean libraryCompilation) {
+      boolean libraryCompilation,
+      DexItemFactory factory) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
+    this.prefixRewritingMapper =
+        rewritingFlags.getRewritePrefix().isEmpty()
+            ? PrefixRewritingMapper.empty()
+            : new DesugarPrefixRewritingMapper(
+                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public boolean supportAllCallbacksFromLibrary() {
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
+  public PrefixRewritingMapper getPrefixRewritingMapper() {
+    return prefixRewritingMapper;
+  }
+
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -68,6 +109,10 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
+  public boolean isSupported(DexReference reference, AppView<?> appView) {
+    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
+  }
+
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index e01def6..a612177 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -103,7 +103,7 @@
 
     HumanDesugaredLibrarySpecification config =
         new HumanDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 9ab05b9..3da0b97 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -6,9 +6,13 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -22,18 +26,25 @@
   private final boolean libraryCompilation;
   private final LegacyTopLevelFlags topLevelFlags;
   private final LegacyRewritingFlags rewritingFlags;
+  private final PrefixRewritingMapper prefixRewritingMapper;
 
   public static LegacyDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
       Map<String, String> prefix, InternalOptions options) {
     return new LegacyDesugaredLibrarySpecification(
         LegacyTopLevelFlags.empty(),
         LegacyRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true);
+        true,
+        options.itemFactory);
   }
 
   public static LegacyDesugaredLibrarySpecification empty() {
     return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false) {
+        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false, null) {
+
+      @Override
+      public boolean isSupported(DexReference reference, AppView<?> appView) {
+        return false;
+      }
 
       @Override
       public boolean isEmptyConfiguration() {
@@ -45,10 +56,16 @@
   public LegacyDesugaredLibrarySpecification(
       LegacyTopLevelFlags topLevelFlags,
       LegacyRewritingFlags rewritingFlags,
-      boolean libraryCompilation) {
+      boolean libraryCompilation,
+      DexItemFactory factory) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
+    this.prefixRewritingMapper =
+        rewritingFlags.getRewritePrefix().isEmpty()
+            ? PrefixRewritingMapper.empty()
+            : new DesugarPrefixRewritingMapper(
+                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public LegacyTopLevelFlags getTopLevelFlags() {
@@ -63,6 +80,10 @@
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
+  public PrefixRewritingMapper getPrefixRewritingMapper() {
+    return prefixRewritingMapper;
+  }
+
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -102,6 +123,10 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
+  public boolean isSupported(DexReference reference, AppView<?> appView) {
+    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
+  }
+
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
index a3afcd9..7cece17 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
@@ -110,7 +110,7 @@
 
     LegacyDesugaredLibrarySpecification config =
         new LegacyDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index e1368e3..501482e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,41 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.Pair;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-
 public class MachineDesugaredLibrarySpecification {
 
   private final boolean libraryCompilation;
   private final MachineTopLevelFlags topLevelFlags;
   private final MachineRewritingFlags rewritingFlags;
 
-  public static MachineDesugaredLibrarySpecification empty() {
-    return new MachineDesugaredLibrarySpecification(
-        false, MachineTopLevelFlags.empty(), MachineRewritingFlags.builder().build()) {
-      @Override
-      public boolean isSupported(DexReference reference) {
-        return false;
-      }
-    };
-  }
-
-  public static MachineDesugaredLibrarySpecification withOnlyRewriteTypeForTesting(
-      Map<DexType, DexType> rewriteTypeForTesting) {
-    MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
-    rewriteTypeForTesting.forEach(builder::rewriteType);
-    return new MachineDesugaredLibrarySpecification(
-        true, MachineTopLevelFlags.empty(), builder.build());
-  }
-
   public MachineDesugaredLibrarySpecification(
       boolean libraryCompilation,
       MachineTopLevelFlags topLevelFlags,
@@ -52,116 +23,11 @@
     return libraryCompilation;
   }
 
-  public AndroidApiLevel getRequiredCompilationAPILevel() {
-    return topLevelFlags.getRequiredCompilationAPILevel();
+  public MachineTopLevelFlags getTopLevelFlags() {
+    return topLevelFlags;
   }
 
-  public String getSynthesizedLibraryClassesPackagePrefix() {
-    return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
-  }
-
-  public String getIdentifier() {
-    return topLevelFlags.getIdentifier();
-  }
-
-  public String getJsonSource() {
-    return topLevelFlags.getJsonSource();
-  }
-
-  public boolean supportAllCallbacksFromLibrary() {
-    return topLevelFlags.supportAllCallbacksFromLibrary();
-  }
-
-  public List<String> getExtraKeepRules() {
-    return topLevelFlags.getExtraKeepRules();
-  }
-
-  public Map<DexType, DexType> getRewriteType() {
-    return rewritingFlags.getRewriteType();
-  }
-
-  public Map<DexType, DexType> getRewriteDerivedTypeOnly() {
-    return rewritingFlags.getRewriteDerivedTypeOnly();
-  }
-
-  public Map<DexMethod, DexMethod> getStaticRetarget() {
-    return rewritingFlags.getStaticRetarget();
-  }
-
-  public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
-    return rewritingFlags.getNonEmulatedVirtualRetarget();
-  }
-
-  public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
-    return rewritingFlags.getEmulatedVirtualRetarget();
-  }
-
-  public void forEachRetargetHolder(Consumer<DexType> consumer) {
-    rewritingFlags.forEachRetargetHolder(consumer);
-  }
-
-  public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
-    return rewritingFlags.getEmulatedInterfaces();
-  }
-
-  public EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
-      DexMethod method) {
-    return rewritingFlags.getEmulatedInterfaceEmulatedDispatchMethodDescriptor(method);
-  }
-
-  public boolean isCustomConversionRewrittenType(DexType type) {
-    return rewritingFlags.isCustomConversionRewrittenType(type);
-  }
-
-  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
-    return rewritingFlags.isEmulatedInterfaceRewrittenType(type);
-  }
-
-  public Map<DexType, List<DexMethod>> getWrappers() {
-    return rewritingFlags.getWrappers();
-  }
-
-  public Map<DexType, DexType> getLegacyBackport() {
-    return rewritingFlags.getLegacyBackport();
-  }
-
-  public Set<DexType> getDontRetarget() {
-    return rewritingFlags.getDontRetarget();
-  }
-
-  public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
-    return rewritingFlags.getCustomConversions();
-  }
-
-  public boolean hasRetargeting() {
-    return rewritingFlags.hasRetargeting();
-  }
-
-  public boolean hasEmulatedInterfaces() {
-    return rewritingFlags.hasEmulatedInterfaces();
-  }
-
-  public boolean isSupported(DexReference reference) {
-    // Support through type rewriting.
-    if (rewritingFlags.getRewriteType().containsKey(reference.getContextType())) {
-      return true;
-    }
-    if (!reference.isDexMethod()) {
-      return false;
-    }
-    // Support through retargeting.
-    DexMethod dexMethod = reference.asDexMethod();
-    if (getStaticRetarget().containsKey(dexMethod)
-        || getNonEmulatedVirtualRetarget().containsKey(dexMethod)
-        || getEmulatedVirtualRetarget().containsKey(dexMethod)) {
-      return true;
-    }
-    // Support through emulated interface.
-    for (EmulatedInterfaceDescriptor descriptor : getEmulatedInterfaces().values()) {
-      if (descriptor.getEmulatedMethods().containsKey(dexMethod)) {
-        return true;
-      }
-    }
-    return false;
+  public MachineRewritingFlags getRewritingFlags() {
+    return rewritingFlags;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index c5c77c6..a6c487e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -11,12 +11,10 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 
 public class MachineRewritingFlags {
 
@@ -96,12 +94,6 @@
     return emulatedVirtualRetarget;
   }
 
-  public void forEachRetargetHolder(Consumer<DexType> consumer) {
-    staticRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
-    nonEmulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
-    emulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
-  }
-
   public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
     return emulatedInterfaces;
   }
@@ -118,37 +110,10 @@
     return dontRetarget;
   }
 
-  public boolean isCustomConversionRewrittenType(DexType type) {
-    return Iterables.any(customConversions.values(), pair -> pair.getFirst() == type);
-  }
-
   public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
     return customConversions;
   }
 
-  public boolean hasRetargeting() {
-    return !staticRetarget.isEmpty()
-        || !nonEmulatedVirtualRetarget.isEmpty()
-        || !emulatedVirtualRetarget.isEmpty();
-  }
-
-  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
-    return Iterables.any(
-        emulatedInterfaces.values(), descriptor -> descriptor.getRewrittenType() == type);
-  }
-
-  public boolean hasEmulatedInterfaces() {
-    return !emulatedInterfaces.isEmpty();
-  }
-
-  EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
-      DexMethod method) {
-    if (!emulatedInterfaces.containsKey(method.getHolderType())) {
-      return null;
-    }
-    return emulatedInterfaces.get(method.getHolderType()).getEmulatedMethods().get(method);
-  }
-
   public static class Builder {
 
     Builder() {}
@@ -172,7 +137,6 @@
     public void rewriteType(DexType src, DexType target) {
       assert src != null;
       assert target != null;
-      assert src != target;
       rewriteType.put(src, target);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
index 0c6a88a..f426219 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableList;
 import java.util.List;
 
 public class MachineTopLevelFlags {
@@ -24,11 +23,6 @@
   private final boolean supportAllCallbacksFromLibrary;
   private final List<String> extraKeepRules;
 
-  public static MachineTopLevelFlags empty() {
-    return new MachineTopLevelFlags(
-        AndroidApiLevel.B, "unused", null, null, false, ImmutableList.of());
-  }
-
   public MachineTopLevelFlags(
       AndroidApiLevel requiredCompilationAPILevel,
       String synthesizedLibraryClassesPackagePrefix,
@@ -60,7 +54,7 @@
     return jsonSource;
   }
 
-  public boolean supportAllCallbacksFromLibrary() {
+  public boolean isSupportAllCallbacksFromLibrary() {
     return supportAllCallbacksFromLibrary;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index 5fcf037..a54cd8d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -129,8 +129,8 @@
     }
     if (appView
         .options()
-        .machineDesugaredLibrarySpecification
-        .getDontRetarget()
+        .desugaredLibrarySpecification
+        .getDontRetargetLibMember()
         .contains(context.getContextType())) {
       return NO_REWRITING;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index eb28d20..11fcfb7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -20,7 +20,7 @@
       AppView<?> appView, RetargetingInfo retargetingInfo) {
     assert appView.options().isDesugaredLibraryCompilation();
     if (retargetingInfo == null || retargetingInfo.getEmulatedVirtualRetarget().isEmpty()) {
-      assert !appView.options().machineDesugaredLibrarySpecification.hasRetargeting();
+      assert appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty();
       return null;
     }
     return new DesugaredLibraryRetargeterL8Synthesizer(appView, retargetingInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
index bfb423a..bbaa4cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
@@ -39,16 +39,16 @@
 public class DesugaredLibraryRetargeterLibraryTypeSynthesizer {
 
   public static void checkForAssumedLibraryTypes(AppView<?> appView) {
-    appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .forEachRetargetHolder(
-            inType -> {
-              DexClass typeClass = appView.definitionFor(inType);
-              if (typeClass == null) {
-                warnMissingRetargetCoreLibraryMember(inType, appView);
-              }
-            });
+    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
+    for (DexString methodName : retargetCoreLibMember.keySet()) {
+      for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+        DexClass typeClass = appView.definitionFor(inType);
+        if (typeClass == null) {
+          warnMissingRetargetCoreLibraryMember(inType, appView);
+        }
+      }
+    }
   }
 
   public static void amendLibraryWithRetargetedMembers(AppView<AppInfoWithClassHierarchy> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index d1e64ec..414c32d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -127,8 +127,8 @@
       }
       if (appView
           .options()
-          .machineDesugaredLibrarySpecification
-          .getDontRetarget()
+          .desugaredLibrarySpecification
+          .getDontRetargetLibMember()
           .contains(clazz.getType())) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
index b7ac366..9e1f5e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
@@ -4,9 +4,24 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 public class RetargetingInfo {
@@ -25,12 +40,15 @@
   }
 
   public static RetargetingInfo get(AppView<?> appView) {
-    MachineDesugaredLibrarySpecification specification =
-        appView.options().machineDesugaredLibrarySpecification;
-    return new RetargetingInfo(
-        specification.getStaticRetarget(),
-        specification.getNonEmulatedVirtualRetarget(),
-        specification.getEmulatedVirtualRetarget());
+    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+      MachineRewritingFlags rewritingFlags =
+          appView.options().testing.machineDesugaredLibrarySpecification.getRewritingFlags();
+      return new RetargetingInfo(
+          rewritingFlags.getStaticRetarget(),
+          rewritingFlags.getNonEmulatedVirtualRetarget(),
+          rewritingFlags.getEmulatedVirtualRetarget());
+    }
+    return new RetargetingInfoBuilder(appView).computeRetargetingInfo();
   }
 
   public Map<DexMethod, DexMethod> getStaticRetarget() {
@@ -44,4 +62,152 @@
   public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
     return emulatedVirtualRetarget;
   }
+
+  private static class RetargetingInfoBuilder {
+
+    private final AppView<?> appView;
+    private final Map<DexMethod, DexMethod> staticRetarget = new IdentityHashMap<>();
+    private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget = new IdentityHashMap<>();
+    private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
+        new IdentityHashMap<>();
+
+    public RetargetingInfoBuilder(AppView<?> appView) {
+      this.appView = appView;
+    }
+
+    private RetargetingInfo computeRetargetingInfo() {
+      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+          appView.options().desugaredLibrarySpecification;
+      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+          desugaredLibrarySpecification.getRetargetCoreLibMember();
+      if (retargetCoreLibMember.isEmpty()) {
+        return new RetargetingInfo(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
+      }
+      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<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
+            for (DexClassAndMethod method : found) {
+              DexMethod methodReference = method.getReference();
+              if (method.getAccessFlags().isStatic()) {
+                staticRetarget.put(
+                    methodReference,
+                    computeRetargetMethod(
+                        methodReference, method.getAccessFlags().isStatic(), newHolder));
+                continue;
+              }
+              if (isEmulatedInterfaceDispatch(method)) {
+                continue;
+              }
+              if (typeClass.isFinal() || method.getAccessFlags().isFinal()) {
+                nonEmulatedVirtualRetarget.put(
+                    methodReference,
+                    computeRetargetMethod(
+                        methodReference, method.getAccessFlags().isStatic(), newHolder));
+              } else {
+                // Virtual rewrites require emulated dispatch for inheritance.
+                // The call is rewritten to the dispatch holder class instead.
+                DexProto newProto = appView.dexItemFactory().prependHolderToProto(methodReference);
+                DexMethod forwardingDexMethod =
+                    appView.dexItemFactory().createMethod(newHolder, newProto, methodName);
+                DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
+                DerivedMethod interfaceMethod =
+                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
+                DerivedMethod dispatchMethod =
+                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_CLASS);
+                emulatedVirtualRetarget.put(
+                    methodReference,
+                    new EmulatedDispatchMethodDescriptor(
+                        interfaceMethod, dispatchMethod, forwardingMethod, new LinkedHashMap<>()));
+              }
+            }
+          }
+        }
+      }
+      if (desugaredLibrarySpecification.isLibraryCompilation()) {
+        // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
+        // to j.u.DesugarArrays.deepEquals0.
+        DexItemFactory itemFactory = appView.options().dexItemFactory();
+        DexString name = itemFactory.createString("deepEquals0");
+        DexProto proto =
+            itemFactory.createProto(
+                itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
+        DexMethod source =
+            itemFactory.createMethod(
+                itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
+        DexMethod target =
+            computeRetargetMethod(
+                source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
+        staticRetarget.put(source, target);
+        // TODO(b/181629049): This is only a workaround rewriting invokes of
+        //  j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
+        name = itemFactory.createString("getTimeZone");
+        proto =
+            itemFactory.createProto(
+                itemFactory.createType("Ljava/util/TimeZone;"),
+                itemFactory.createType("Ljava/time/ZoneId;"));
+        source =
+            itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
+        target =
+            computeRetargetMethod(
+                source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
+        staticRetarget.put(source, target);
+      }
+      return new RetargetingInfo(
+          ImmutableMap.copyOf(staticRetarget),
+          ImmutableMap.copyOf(nonEmulatedVirtualRetarget),
+          emulatedVirtualRetarget);
+    }
+
+    DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
+      DexItemFactory factory = appView.dexItemFactory();
+      DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
+      return factory.createMethod(newHolder, newProto, method.getName());
+    }
+
+    private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
+      // Answers true if this method is already managed through emulated interface dispatch.
+      Map<DexType, DexType> emulateLibraryInterface =
+          appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
+      if (emulateLibraryInterface.isEmpty()) {
+        return false;
+      }
+      DexMethod methodToFind = method.getReference();
+      // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
+      // the method, answers true.
+      WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
+      while (worklist.hasNext()) {
+        DexClass clazz = worklist.next();
+        if (clazz.isInterface()
+            && emulateLibraryInterface.containsKey(clazz.getType())
+            && clazz.lookupMethod(methodToFind) != null) {
+          return true;
+        }
+        // All super types are library class, or we are doing L8 compilation.
+        clazz.forEachImmediateSupertype(
+            superType -> {
+              DexClass superClass = appView.definitionFor(superType);
+              if (superClass != null) {
+                worklist.addIfNotSeen(superClass);
+              }
+            });
+      }
+      return false;
+    }
+
+    private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
+      List<DexClassAndMethod> found = new ArrayList<>();
+      clazz.forEachClassMethodMatching(
+          definition -> definition.getName() == methodName, found::add);
+      assert !found.isEmpty()
+          : "Should have found a method (library specifications) for "
+              + clazz.toSourceString()
+              + "."
+              + methodName
+              + ". Maybe the library used for the compilation should be newer.";
+      return found;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 04cc12b..2411ba0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
@@ -43,10 +44,10 @@
     Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
     emulatedInterfaceHierarchy = processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
     for (DexType itf : emulateInterfaces.keySet()) {
-      DexClass itfClass = appInfo.contextIndependentDefinitionFor(itf);
+      DexProgramClass itfClass = appInfo.contextIndependentDefinitionFor(itf).asProgramClass();
       assert itfClass != null;
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods = new IdentityHashMap<>();
-      itfClass.forEachClassMethodMatching(
+      itfClass.forEachProgramVirtualMethodMatching(
           m -> m.isDefaultMethod() && !dontRewriteInvocation.contains(m.getReference()),
           method ->
               emulatedMethods.put(
@@ -103,6 +104,7 @@
       for (int i = subInterfaces.size() - 1; i >= 0; i--) {
         DexClass subInterfaceClass = appInfo.definitionFor(subInterfaces.get(i));
         assert subInterfaceClass != null;
+        assert subInterfaceClass.isProgramClass();
         // Else computation of subInterface would have failed.
         // if the method is implemented, extra dispatch is required.
         DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 92efb24..6ff7428 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -5,31 +5,22 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
 import java.util.Map;
 
 public class HumanToMachinePrefixConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private final MachineRewritingFlags.Builder builder;
-  private final String synthesizedPrefix;
-  private final Map<DexType, DexType> reverse = new IdentityHashMap<>();
 
-  public HumanToMachinePrefixConverter(
-      AppInfoWithClassHierarchy appInfo,
-      MachineRewritingFlags.Builder builder,
-      String synthesizedPrefix) {
+  public HumanToMachinePrefixConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
-    this.builder = builder;
-    this.synthesizedPrefix = synthesizedPrefix;
   }
 
   private DexString toDescriptorPrefix(String prefix) {
@@ -38,30 +29,20 @@
         .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
   }
 
-  public void convertPrefixFlags(HumanRewritingFlags rewritingFlags) {
+  public void convertPrefixFlags(
+      HumanRewritingFlags rewritingFlags,
+      MachineRewritingFlags.Builder builder,
+      String synthesizedPrefix) {
     Map<DexString, DexString> descriptorPrefix = convertRewritePrefix(rewritingFlags);
-    rewriteClasses(descriptorPrefix);
-    rewriteValues(descriptorPrefix, rewritingFlags.getRetargetCoreLibMember());
-    rewriteValues(descriptorPrefix, rewritingFlags.getCustomConversions());
-    rewriteEmulatedInterface(rewritingFlags.getEmulateLibraryInterface());
-    rewriteRetargetKeys(rewritingFlags.getRetargetCoreLibMember());
-    rewriteReverse(descriptorPrefix);
+    rewriteClasses(descriptorPrefix, builder);
+    rewriteValues(descriptorPrefix, builder, rewritingFlags.getRetargetCoreLibMember());
+    rewriteValues(descriptorPrefix, builder, rewritingFlags.getCustomConversions());
+    rewriteEmulatedInterface(builder, rewritingFlags.getEmulateLibraryInterface());
+    rewriteRetargetKeys(builder, rewritingFlags.getRetargetCoreLibMember(), synthesizedPrefix);
   }
 
-  // For custom conversions, this is responsible in rewriting backward.
-  private void rewriteReverse(Map<DexString, DexString> descriptorPrefix) {
-    reverse.forEach(
-        (rewrittenType, type) -> {
-          DexType backwardRewrittenType = rewrittenType(descriptorPrefix, rewrittenType);
-          if (backwardRewrittenType != null) {
-            assert backwardRewrittenType == type;
-            builder.rewriteType(rewrittenType, type);
-          }
-        });
-  }
-
-  public DexType convertJavaNameToDesugaredLibrary(DexType type) {
-    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(synthesizedPrefix);
+  public DexType convertJavaNameToDesugaredLibrary(DexType type, String prefix) {
+    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(prefix);
     String interfaceType = type.toString();
     int firstPackage = interfaceType.indexOf('.');
     return appInfo
@@ -71,49 +52,40 @@
                 convertedPrefix + interfaceType.substring(firstPackage + 1)));
   }
 
-  private void rewriteRetargetKeys(Map<DexMethod, DexType> retarget) {
+  private void rewriteRetargetKeys(
+      MachineRewritingFlags.Builder builder, Map<DexMethod, DexType> retarget, String prefix) {
     for (DexMethod dexMethod : retarget.keySet()) {
-      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder);
+      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder, prefix);
       builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
     }
   }
 
-  private void rewriteEmulatedInterface(Map<DexType, DexType> emulateLibraryInterface) {
+  private void rewriteEmulatedInterface(
+      MachineRewritingFlags.Builder builder, Map<DexType, DexType> emulateLibraryInterface) {
     emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
   }
 
-  private void rewriteType(DexType type, DexType rewrittenType) {
-    builder.rewriteType(type, rewrittenType);
-    reverse.put(rewrittenType, type);
-  }
-
   private void rewriteValues(
       Map<DexString, DexString> descriptorPrefix,
+      MachineRewritingFlags.Builder builder,
       Map<?, DexType> flags) {
     for (DexType type : flags.values()) {
       DexType rewrittenType = rewrittenType(descriptorPrefix, type);
       if (rewrittenType != null) {
-        rewriteType(type, rewrittenType);
+        builder.rewriteType(type, rewrittenType);
       }
     }
   }
 
-  private void rewriteClasses(Map<DexString, DexString> descriptorPrefix) {
-    for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
-      rewriteClass(descriptorPrefix, clazz);
+  private void rewriteClasses(
+      Map<DexString, DexString> descriptorPrefix, MachineRewritingFlags.Builder builder) {
+    for (DexProgramClass clazz : appInfo.classes()) {
+      DexType type = clazz.type;
+      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+      if (rewrittenType != null) {
+        builder.rewriteType(type, rewrittenType);
+      }
     }
-    for (DexClass clazz : appInfo.classes()) {
-      rewriteClass(descriptorPrefix, clazz);
-    }
-  }
-
-  private void rewriteClass(Map<DexString, DexString> descriptorPrefix, DexClass clazz) {
-    DexType type = clazz.type;
-    DexType rewrittenType = rewrittenType(descriptorPrefix, type);
-    if (rewrittenType == null) {
-      return;
-    }
-    rewriteType(type, rewrittenType);
   }
 
   private DexType rewrittenType(Map<DexString, DexString> descriptorPrefix, DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index b5c1ac3..6dce945 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -92,14 +92,10 @@
       DexClass subclass = appInfo.definitionFor(subtype);
       MethodResolutionResult resolutionResult =
           appInfo.resolveMethodOn(subclass, src.getReference());
-      // The resolution is not successful when compiling to dex if the method rewritten is missing
-      // in Android.jar.
-      assert !resolutionResult.isSuccessfulMemberResolutionResult()
-          || resolutionResult.getResolvedMethod().getReference() == src.getReference()
-          // There is a difference in the sql library between Android.jar and the JDK which leads
-          // to this resolution when compiling Cf to Cf while the methods do not exist in Android.
-          || (resolutionResult.getResolvedMethod().getHolderType().toString().contains("java.sql")
-              && resolutionResult.getResolvedMethod().getName().toString().equals("toInstant"));
+      if (resolutionResult.isSuccessfulMemberResolutionResult()
+          && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
+        assert false; // Unsupported.
+      }
     }
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index 23824c5..bc3a663 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -24,51 +22,15 @@
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.util.List;
 import java.util.concurrent.ExecutorService;
 
 public class HumanToMachineSpecificationConverter {
 
   public MachineDesugaredLibrarySpecification convert(
-      HumanDesugaredLibrarySpecification humanSpec,
-      List<ProgramResourceProvider> desugaredJDKLib,
-      List<ClassFileResourceProvider> library,
-      InternalOptions options)
+      HumanDesugaredLibrarySpecification humanSpec, Path androidLib, InternalOptions options)
       throws IOException {
-    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
-    AndroidApp.Builder builder = AndroidApp.builder();
-    for (ClassFileResourceProvider classFileResourceProvider : library) {
-      builder.addLibraryResourceProvider(classFileResourceProvider);
-    }
-    if (humanSpec.isLibraryCompilation()) {
-      for (ProgramResourceProvider programResourceProvider : desugaredJDKLib) {
-        builder.addProgramResourceProvider(programResourceProvider);
-      }
-    }
-    return internalConvert(humanSpec, builder.build(), options);
-  }
-
-  public MachineDesugaredLibrarySpecification convert(
-      HumanDesugaredLibrarySpecification humanSpec,
-      Path desugaredJDKLib,
-      Path androidLib,
-      InternalOptions options)
-      throws IOException {
-    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
-    AndroidApp.Builder builder = AndroidApp.builder();
-    if (humanSpec.isLibraryCompilation()) {
-      builder.addProgramFile(desugaredJDKLib);
-    }
-    AndroidApp inputApp = builder.addLibraryFile(androidLib).build();
-    return internalConvert(humanSpec, inputApp, options);
-  }
-
-  public MachineDesugaredLibrarySpecification internalConvert(
-      HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
-      throws IOException {
-    DexApplication app = readApp(inputApp, options);
+    DexApplication app = readApp(androidLib, options);
     AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
-    LibraryValidator.validate(app, humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     MachineRewritingFlags machineRewritingFlags =
         convertRewritingFlags(
             humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
@@ -97,8 +59,8 @@
     new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
         .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
-    new HumanToMachinePrefixConverter(appInfo, builder, synthesizedPrefix)
-        .convertPrefixFlags(rewritingFlags);
+    new HumanToMachinePrefixConverter(appInfo)
+        .convertPrefixFlags(rewritingFlags, builder, synthesizedPrefix);
     new HumanToMachineWrapperConverter(appInfo).convertWrappers(rewritingFlags, builder);
     rewritingFlags
         .getCustomConversions()
@@ -113,8 +75,10 @@
     return builder.build();
   }
 
-  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
-    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
+    AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
+    ApplicationReader applicationReader =
+        new ApplicationReader(androidApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index e5c9b42..34bf5fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
-import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -66,9 +65,7 @@
       InternalOptions options)
       throws IOException {
     Origin origin = legacySpec.getOrigin();
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    DexApplication app = readApp(androidApp, options);
-    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    DexApplication app = readApp(androidLib, options);
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
@@ -84,30 +81,11 @@
   }
 
   public HumanDesugaredLibrarySpecification convert(
-      LegacyDesugaredLibrarySpecification legacySpec,
-      List<ClassFileResourceProvider> library,
-      InternalOptions options)
-      throws IOException {
-    AndroidApp.Builder builder = AndroidApp.builder();
-    for (ClassFileResourceProvider classFileResourceProvider : library) {
-      builder.addLibraryResourceProvider(classFileResourceProvider);
-    }
-    return internalConvert(legacySpec, builder.build(), options);
-  }
-
-  public HumanDesugaredLibrarySpecification convert(
       LegacyDesugaredLibrarySpecification legacySpec, Path androidLib, InternalOptions options)
       throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    return internalConvert(legacySpec, androidApp, options);
-  }
-
-  public HumanDesugaredLibrarySpecification internalConvert(
-      LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
-      throws IOException {
-    DexApplication app = readApp(inputApp, options);
-    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    DexApplication app = readApp(androidLib, options);
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
+
     // The origin is not maintained in non multi-level specifications.
     // It should not matter since the origin is used to report invalid specifications, and
     // converting non multi-level specifications should be performed only with *valid*
@@ -117,7 +95,10 @@
     HumanRewritingFlags humanRewritingFlags =
         convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
     return new HumanDesugaredLibrarySpecification(
-        humanTopLevelFlags, humanRewritingFlags, legacySpec.isLibraryCompilation());
+        humanTopLevelFlags,
+        humanRewritingFlags,
+        legacySpec.isLibraryCompilation(),
+        app.dexItemFactory());
   }
 
   private void legacyLibraryFlagHacks(
@@ -153,8 +134,10 @@
     libraryFlags.put(level, builder.build());
   }
 
-  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
-    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
+    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
+    ApplicationReader applicationReader =
+        new ApplicationReader(androidApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
deleted file mode 100644
index b415c354..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2022, 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.desugaredlibrary.specificationconversion;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.AndroidApiLevel;
-
-public class LibraryValidator {
-
-  // Estimates if the library passed is at the expected minimum level, if it is not, raise
-  // a warning.
-  public static void validate(DexApplication app, AndroidApiLevel requiredCompilationAPILevel) {
-    DexType levelType;
-    if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.O)) {
-      levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
-    } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) {
-      levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
-    } else {
-      app.options.reporter.warning(
-          "Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
-      return;
-    }
-    DexClass dexClass = app.definitionFor(levelType);
-    if (dexClass == null) {
-      app.options.reporter.warning(
-          "Desugared library requires to be compiled with a library file of API greater or equal to"
-              + " "
-              + requiredCompilationAPILevel
-              + ", but it seems the library file passed is of a lower API.");
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 33f7037..758812e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -24,10 +24,10 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -49,6 +49,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import org.objectweb.asm.Opcodes;
@@ -375,7 +376,12 @@
     this.dexItemFactory = appView.dexItemFactory();
     this.helper = new InterfaceDesugaringSyntheticHelper(appView);
     needsLibraryInfo =
-        appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
+        !appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()
+            || !appView
+                .options()
+                .desugaredLibrarySpecification
+                .getRetargetCoreLibMember()
+                .isEmpty();
     this.isLiveMethod = isLiveMethod;
   }
 
@@ -504,7 +510,7 @@
       DexClass iface = appView.definitionFor(emulatedInterface);
       if (iface != null) {
         assert iface.isLibraryClass()
-            || appView.options().machineDesugaredLibrarySpecification.isLibraryCompilation();
+            || appView.options().desugaredLibrarySpecification.isLibraryCompilation();
         workList.addIfNotSeen(iface.getInterfaces());
       }
     }
@@ -664,10 +670,10 @@
       resolveForwardForSignature(
           clazz,
           wrapper.get(),
-          (target, forward) -> {
+          target -> {
             if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) {
               additionalForwards.add(target);
-              addForwardingMethod(target, forward, clazz);
+              addForwardingMethod(target, clazz);
             }
           });
     }
@@ -676,7 +682,7 @@
   // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
-      DexClass clazz, DexMethod method, BiConsumer<DexClassAndMethod, DexMethod> addForward) {
+      DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method);
     if (resolutionResult.isFailedResolution()
@@ -716,22 +722,50 @@
         resolutionResult.lookupVirtualDispatchTarget(clazz, appInfo);
     assert virtualDispatchTarget != null;
 
-    // If resolution targets a default interface method, forward it.
-    if (virtualDispatchTarget.isDefaultMethod()) {
-      addForward.accept(
-          virtualDispatchTarget,
-          helper.ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget).getReference());
+    // Don't forward if the target is explicitly marked as 'dont-rewrite'
+    if (dontRewrite(virtualDispatchTarget)) {
       return;
     }
 
-    DerivedMethod forwardingMethod =
-        helper.computeEmulatedInterfaceForwardingMethod(
-            virtualDispatchTarget.getHolder(), virtualDispatchTarget);
-    if (forwardingMethod != null) {
-      DexMethod concreteForwardingMethod =
-          helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod);
-      addForward.accept(virtualDispatchTarget, concreteForwardingMethod);
+    // If resolution targets a default interface method, forward it.
+    if (virtualDispatchTarget.isDefaultMethod()) {
+      addForward.accept(virtualDispatchTarget);
+      return;
     }
+
+    // Remaining edge cases only pertain to desugaring of library methods.
+    if (!virtualDispatchTarget.isLibraryMethod() || ignoreLibraryInfo()) {
+      return;
+    }
+
+    LibraryMethod libraryMethod = virtualDispatchTarget.asLibraryMethod();
+    if (isRetargetMethod(libraryMethod)) {
+      addForward.accept(virtualDispatchTarget);
+      return;
+    }
+
+    // If target is a non-interface library class it may be an emulated interface,
+    // except on a rewritten type, where L8 has already dealt with the desugaring.
+    if (!libraryMethod.getHolder().isInterface()
+        && !appView.rewritePrefix.hasRewrittenType(libraryMethod.getHolderType(), appView)) {
+      // Here we use step-3 of resolution to find a maximally specific default interface method.
+      DexClassAndMethod result =
+          appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
+      if (result != null && helper.isEmulatedInterface(result.getHolderType())) {
+        addForward.accept(result);
+      }
+    }
+  }
+
+  private boolean isRetargetMethod(LibraryMethod method) {
+    assert needsLibraryInfo();
+    assert method.getDefinition().isNonPrivateVirtualMethod();
+    return !method.getAccessFlags().isFinal()
+        && appView.options().desugaredLibrarySpecification.retargetMethod(method, appView) != null;
+  }
+
+  private boolean dontRewrite(DexClassAndMethod method) {
+    return needsLibraryInfo() && method.getHolder().isLibraryClass() && helper.dontRewrite(method);
   }
 
   // Construction of actual forwarding methods.
@@ -796,8 +830,7 @@
 
   // Note: The parameter 'target' may be a public method on a class in case of desugared
   // library retargeting (See below target.isInterface check).
-  private void addForwardingMethod(
-      DexClassAndMethod target, DexMethod forwardMethod, DexClass clazz) {
+  private void addForwardingMethod(DexClassAndMethod target, DexClass clazz) {
     if (!clazz.isProgramClass()) {
       return;
     }
@@ -814,6 +847,10 @@
     // 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 =
+        target.getHolder().isInterface()
+            ? helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
+            : appView.options().desugaredLibrarySpecification.retargetMethod(target, appView);
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
             target.getDefinition(), clazz, forwardMethod, dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
index d06fee7..74d94e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.utils.IterableUtils;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -26,15 +25,8 @@
 
   public EmulatedInterfaceApplicationRewriter(AppView<?> appView) {
     this.appView = appView;
-    emulatedInterfaces = new IdentityHashMap<>();
-    appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .getEmulatedInterfaces()
-        .forEach(
-            (ei, descriptor) -> {
-              emulatedInterfaces.put(ei, descriptor.getRewrittenType());
-            });
+    this.emulatedInterfaces =
+        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
   }
 
   public void rewriteApplication(DexApplication.Builder<?> builder) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
index 6636e1e..a3e0164 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
@@ -4,12 +4,10 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-
 public class InterfaceDesugaringForTesting {
 
   public static String getEmulateLibraryClassNameSuffix() {
-    return SyntheticKind.EMULATED_INTERFACE_CLASS.descriptor;
+    return InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
   }
 
   public static String getCompanionClassNameSuffix() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index cb4a537..77d99e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -24,7 +24,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
@@ -32,20 +31,19 @@
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowNullCode;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.ImmutableList;
+import java.util.Map;
+import java.util.Set;
 import java.util.function.Predicate;
 
 public class InterfaceDesugaringSyntheticHelper {
@@ -59,37 +57,37 @@
   }
 
   // Use InterfaceDesugaringForTesting for public accesses in tests.
+  static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
   static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
   static final String DEFAULT_METHOD_PREFIX = "$default$";
   static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final AppView<?> appView;
+  private final Map<DexType, DexType> emulatedInterfaces;
   private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
 
   public InterfaceDesugaringSyntheticHelper(AppView<?> appView) {
     this.appView = appView;
+    emulatedInterfaces =
+        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
+
     this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
   }
 
   boolean isEmulatedInterface(DexType itf) {
-    return appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .getEmulatedInterfaces()
-        .containsKey(itf);
+    return emulatedInterfaces.containsKey(itf);
   }
 
   boolean isRewrittenEmulatedInterface(DexType itf) {
-    return appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .isEmulatedInterfaceRewrittenType(itf);
+    return emulatedInterfaces.containsValue(itf);
+  }
+
+  Set<DexType> getEmulatedInterfaces() {
+    return emulatedInterfaces.keySet();
   }
 
   DexType getEmulatedInterface(DexType type) {
-    EmulatedInterfaceDescriptor interfaceDescriptor =
-        appView.options().machineDesugaredLibrarySpecification.getEmulatedInterfaces().get(type);
-    return interfaceDescriptor == null ? null : interfaceDescriptor.getRewrittenType();
+    return emulatedInterfaces.get(type);
   }
 
   boolean isInDesugaredLibrary(DexClass clazz) {
@@ -100,6 +98,17 @@
     return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
   }
 
+  boolean dontRewrite(DexClassAndMethod method) {
+    for (Pair<DexType, DexString> dontRewrite :
+        appView.options().desugaredLibrarySpecification.getDontRewriteInvocation()) {
+      if (method.getHolderType() == dontRewrite.getFirst()
+          && method.getName() == dontRewrite.getSecond()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   final boolean isCompatibleDefaultMethod(DexEncodedMethod method) {
     assert !method.accessFlags.isConstructor();
     assert !method.accessFlags.isStatic();
@@ -120,15 +129,25 @@
     return true;
   }
 
-  DexMethod emulatedInterfaceDispatchMethod(DerivedMethod method, DexType holder) {
-    assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
-    DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
-    return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
+  public DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
+    DexItemFactory factory = appView.dexItemFactory();
+    return factory.createMethod(
+        getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
+        factory.prependTypeToProto(method.getHolderType(), method.getProto()),
+        method.getName());
   }
 
-  DexMethod emulatedInterfaceInterfaceMethod(DerivedMethod method) {
-    assert method.getHolderKind() == null;
-    return method.getMethod();
+  private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) {
+    return descriptor.substring(0, descriptor.length() - 1)
+        + EMULATE_LIBRARY_CLASS_NAME_SUFFIX
+        + ";";
+  }
+
+  public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
+    assert type.isClassType();
+    String descriptor = type.descriptor.toString();
+    String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
+    return factory.createSynthesizedType(elTypeDescriptor);
   }
 
   public static String getCompanionClassDescriptor(String descriptor) {
@@ -148,6 +167,10 @@
     return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
   }
 
+  public static boolean isEmulatedLibraryClassType(DexType type) {
+    return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
+  }
+
   // Gets the interface class for a companion class `type`.
   DexType getInterfaceClassType(DexType type) {
     return getInterfaceClassType(type, appView.dexItemFactory());
@@ -174,91 +197,9 @@
             SyntheticClassBuilder::setInterface);
   }
 
-  DexClassAndMethod lookupMaximallySpecificIncludingSelf(
-      DexClass initialResolutionHolder, DexClassAndMethod method) {
-    assert method.getHolderType().isClassType();
-    if (method.getHolder().isInterface()) {
-      return method;
-    }
-    return appView
-        .appInfoForDesugaring()
-        .lookupMaximallySpecificMethod(initialResolutionHolder, method.getReference());
-  }
-
-  EmulatedDispatchMethodDescriptor getEmulatedDispatchDescriptor(
-      DexClass initialResolutionHolder, DexClassAndMethod method) {
-    if (method == null) {
-      return null;
-    }
-    assert initialResolutionHolder != null;
-    // Outside L8 compilation, only library methods can lead to emulated interface dispatch.
-    if (!method.isLibraryMethod() && !appView.options().isDesugaredLibraryCompilation()) {
-      return null;
-    }
-    DexClassAndMethod maximallySpecificMethod =
-        lookupMaximallySpecificIncludingSelf(initialResolutionHolder, method);
-    if (maximallySpecificMethod == null) {
-      return null;
-    }
-    EmulatedDispatchMethodDescriptor descriptor =
-        appView
-            .options()
-            .machineDesugaredLibrarySpecification
-            .getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
-                maximallySpecificMethod.getReference());
-    if (!appView.options().isDesugaredLibraryCompilation()) {
-      return descriptor;
-    }
-    return requiresEmulatedDispatchInL8(method, descriptor) ? descriptor : null;
-  }
-
-  private boolean requiresEmulatedDispatchInL8(
-      DexClassAndMethod method, EmulatedDispatchMethodDescriptor descriptor) {
-    return method.isLibraryMethod()
-        || isEmulatedInterface(method.getHolderType())
-        || (descriptor != null
-            && descriptor.getDispatchCases().containsKey(method.getHolderType()));
-  }
-
-  DerivedMethod computeEmulatedInterfaceDispatchMethod(MethodResolutionResult resolutionResult) {
-    EmulatedDispatchMethodDescriptor descriptor =
-        getEmulatedDispatchDescriptor(
-            resolutionResult.getInitialResolutionHolder(), resolutionResult.getResolutionPair());
-    return descriptor == null ? null : descriptor.getEmulatedDispatchMethod();
-  }
-
-  DerivedMethod computeEmulatedInterfaceForwardingMethod(
-      DexClass initialResolutionHolder, DexClassAndMethod method) {
-    EmulatedDispatchMethodDescriptor descriptor =
-        getEmulatedDispatchDescriptor(initialResolutionHolder, method);
-    if (descriptor == null) {
-      return null;
-    }
-    return descriptor.getDispatchCases().containsKey(method.getHolderType())
-        ? descriptor.getDispatchCases().get(method.getHolderType())
-        : descriptor.getForwardingMethod();
-  }
-
-  DexMethod ensureEmulatedInterfaceForwardingMethod(DerivedMethod method) {
-    if (method.getHolderKind() == null) {
-      return method.getMethod();
-    }
-    assert method.getHolderKind() == SyntheticKind.COMPANION_CLASS;
-    DexClassAndMethod resolvedMethod =
-        appView.appInfoForDesugaring().resolveMethod(method.getMethod(), true).getResolutionPair();
-    return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod).getReference();
-  }
-
-  DexClassAndMethod ensureEmulatedInterfaceDispatchMethod(
-      DerivedMethod emulatedDispatchMethod,
-      ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
-    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
-    DexClassAndMethod method =
-        appView
-            .appInfoForDesugaring()
-            .resolveMethod(emulatedDispatchMethod.getMethod(), true)
-            .getResolutionPair();
-    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+  DexClassAndMethod ensureEmulatedInterfaceMethod(
+      DexClassAndMethod method, ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
+    DexMethod emulatedInterfaceMethod = emulateInterfaceLibraryMethod(method);
     if (method.isProgramMethod()) {
       assert appView.options().isDesugaredLibraryCompilation();
       DexProgramClass emulatedInterface =
@@ -268,15 +209,8 @@
                   SyntheticKind.EMULATED_INTERFACE_CLASS,
                   method.asProgramMethod().getHolder(),
                   appView);
-      DexMethod emulatedInterfaceMethod =
-          emulatedInterfaceDispatchMethod(emulatedDispatchMethod, emulatedInterface.type);
-      assert emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod) != null;
       return emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod);
     }
-    // The holder is not used.
-    DexMethod emulatedInterfaceMethod =
-        emulatedInterfaceDispatchMethod(
-            emulatedDispatchMethod, appView.dexItemFactory().objectType);
     return appView
         .getSyntheticItems()
         .ensureFixedClasspathClassMethod(
@@ -577,7 +511,7 @@
       return appView.rewritePrefix.hasRewrittenType(type, appView)
           || descriptor.endsWith(companionClassNameDescriptorSuffix)
           || isRewrittenEmulatedInterface(type)
-          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
+          || options.desugaredLibrarySpecification.getCustomConversions().containsValue(type)
           || appView.getDontWarnConfiguration().matches(type);
     };
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 0227b1f..6740441 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -35,8 +35,7 @@
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
@@ -54,6 +53,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 //
@@ -119,31 +119,18 @@
   }
 
   public static void checkForAssumedLibraryTypes(AppInfo appInfo, InternalOptions options) {
-    MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
-        options.machineDesugaredLibrarySpecification;
-    machineDesugaredLibrarySpecification
-        .getEmulatedInterfaces()
-        .forEach(
-            (ei, descriptor) -> {
-              registerType(appInfo, ei);
-              registerType(appInfo, descriptor.getRewrittenType());
-              descriptor
-                  .getEmulatedMethods()
-                  .forEach(
-                      (method, methodDescriptor) -> {
-                        methodDescriptor
-                            .getDispatchCases()
-                            .keySet()
-                            .forEach(t -> registerType(appInfo, t));
-                      });
-            });
-    machineDesugaredLibrarySpecification
-        .getCustomConversions()
-        .forEach(
-            (type, conversionPair) -> {
-              registerType(appInfo, type);
-              registerType(appInfo, conversionPair.getFirst());
-            });
+    LegacyDesugaredLibrarySpecification spec = options.desugaredLibrarySpecification;
+    BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo);
+    spec.getEmulateLibraryInterface().forEach(registerEntry);
+    spec.getCustomConversions().forEach(registerEntry);
+    spec.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry));
+  }
+
+  private static BiConsumer<DexType, DexType> registerMapEntry(AppInfo appInfo) {
+    return (key, value) -> {
+      registerType(appInfo, key);
+      registerType(appInfo, value);
+    };
   }
 
   private static void registerType(AppInfo appInfo, DexType type) {
@@ -169,9 +156,9 @@
   }
 
   private void initializeEmulatedInterfaceVariables() {
-    Set<DexType> emulateLibraryInterface =
-        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet();
-    for (DexType interfaceType : emulateLibraryInterface) {
+    Map<DexType, DexType> emulateLibraryInterface =
+        options.desugaredLibrarySpecification.getEmulateLibraryInterface();
+    for (DexType interfaceType : emulateLibraryInterface.keySet()) {
       DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
       if (emulatedInterfaceClass != null) {
         for (DexEncodedMethod encodedMethod :
@@ -462,27 +449,24 @@
   }
 
   private DesugarDescription computeEmulatedInterfaceVirtualDispatchOrNull(CfInvoke invoke) {
-    MethodResolutionResult resolutionResult =
-        appView.appInfoForDesugaring().resolveMethod(invoke.getMethod(), invoke.isInterface());
-    DerivedMethod emulatedDispatchMethod =
-        helper.computeEmulatedInterfaceDispatchMethod(resolutionResult);
-    if (emulatedDispatchMethod == null) {
-      return null;
+    DexClassAndMethod defaultMethod =
+        defaultMethodForEmulatedDispatchOrNull(invoke.getMethod(), invoke.isInterface());
+    if (defaultMethod != null) {
+      return DesugarDescription.builder()
+          .setDesugarRewrite(
+              (freshLocalProvider,
+                  localStackAllocator,
+                  eventConsumer,
+                  context1,
+                  methodProcessingContext,
+                  dexItemFactory) ->
+                  getInvokeStaticInstructions(
+                      helper
+                          .ensureEmulatedInterfaceMethod(defaultMethod, eventConsumer)
+                          .getReference()))
+          .build();
     }
-    return DesugarDescription.builder()
-        .setDesugarRewrite(
-            (freshLocalProvider,
-                localStackAllocator,
-                eventConsumer,
-                context1,
-                methodProcessingContext,
-                dexItemFactory) ->
-                getInvokeStaticInstructions(
-                    helper
-                        .ensureEmulatedInterfaceDispatchMethod(
-                            emulatedDispatchMethod, eventConsumer)
-                        .getReference()))
-        .build();
+    return null;
   }
 
   private DesugarDescription computeInvokeDirect(
@@ -720,49 +704,103 @@
 
   private DesugarDescription computeEmulatedInterfaceInvokeSpecial(
       DexClass clazz, DexMethod invokedMethod, ProgramMethod context) {
-    DexClassAndMethod superTarget =
-        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
-    if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
-      if (superTarget != null && superTarget.getDefinition().isDefaultMethod()) {
-        DexClass holder = superTarget.getHolder();
-        if (holder.isLibraryClass() && holder.isInterface()) {
-          return DesugarDescription.builder()
-              .setDesugarRewrite(
-                  (freshLocalProvider,
-                      localStackAllocator,
-                      eventConsumer,
-                      context13,
-                      methodProcessingContext,
-                      dexItemFactory) -> {
-                    DexClassAndMethod companionTarget =
-                        helper.ensureDefaultAsMethodOfCompanionClassStub(superTarget);
-                    acceptCompanionMethod(superTarget, companionTarget, eventConsumer);
-                    return getInvokeStaticInstructions(companionTarget.getReference());
-                  })
-              .build();
+    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+    if (emulatedItf == null) {
+      if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
+        DexClassAndMethod target =
+            appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+        if (target != null && target.getDefinition().isDefaultMethod()) {
+          DexClass holder = target.getHolder();
+          if (holder.isLibraryClass() && holder.isInterface()) {
+            return DesugarDescription.builder()
+                .setDesugarRewrite(
+                    (freshLocalProvider,
+                        localStackAllocator,
+                        eventConsumer,
+                        context13,
+                        methodProcessingContext,
+                        dexItemFactory) -> {
+                      DexClassAndMethod companionTarget =
+                          helper.ensureDefaultAsMethodOfCompanionClassStub(target);
+                      acceptCompanionMethod(target, companionTarget, eventConsumer);
+                      return getInvokeStaticInstructions(companionTarget.getReference());
+                    })
+                .build();
+          }
         }
       }
+      return DesugarDescription.nothing();
     }
     // That invoke super may not resolve since the super method may not be present
     // since it's in the emulated interface. We need to force resolution. If it resolves
     // to a library method, then it needs to be rewritten.
     // If it resolves to a program overrides, the invoke-super can remain.
-    DerivedMethod forwardingMethod =
-        helper.computeEmulatedInterfaceForwardingMethod(clazz, superTarget);
-    if (forwardingMethod == null) {
-      return DesugarDescription.nothing();
+    DexClassAndMethod superTarget =
+        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+    if (superTarget != null && superTarget.isLibraryMethod()) {
+      // Rewriting is required because the super invoke resolves into a missing
+      // method (method is on desugared library). Find out if it needs to be
+      // retargeted or if it just calls a companion class method and rewrite.
+      DexMethod retargetMethod =
+          options.desugaredLibrarySpecification.retargetMethod(superTarget, appView);
+      if (retargetMethod != null) {
+        return DesugarDescription.builder()
+            .setDesugarRewrite(
+                (freshLocalProvider,
+                    localStackAllocator,
+                    eventConsumer,
+                    context14,
+                    methodProcessingContext,
+                    dexItemFactory) -> getInvokeStaticInstructions(retargetMethod))
+            .build();
+      }
+      DexClassAndMethod emulatedMethod =
+          superTarget.getReference().lookupMemberOnClass(appView.definitionFor(emulatedItf));
+      if (emulatedMethod == null) {
+        assert false;
+        return DesugarDescription.nothing();
+      }
+      return DesugarDescription.builder()
+          .setDesugarRewrite(
+              (freshLocalProvider,
+                  localStackAllocator,
+                  eventConsumer,
+                  context15,
+                  methodProcessingContext,
+                  dexItemFactory) -> {
+                DexClassAndMethod companionMethod =
+                    helper.ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
+                return getInvokeStaticInstructions(companionMethod.getReference());
+              })
+          .build();
     }
-    return DesugarDescription.builder()
-        .setDesugarRewrite(
-            (freshLocalProvider,
-                localStackAllocator,
-                eventConsumer,
-                context14,
-                methodProcessingContext,
-                dexItemFactory) ->
-                getInvokeStaticInstructions(
-                    helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod)))
-        .build();
+    return DesugarDescription.nothing();
+  }
+
+  private DexClassAndMethod defaultMethodForEmulatedDispatchOrNull(
+      DexMethod invokedMethod, boolean interfaceBit) {
+    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+    if (emulatedItf == null) {
+      return null;
+    }
+    // The call potentially ends up in a library class, in which case we need to rewrite, since the
+    // code may be in the desugared library.
+    SingleResolutionResult resolution =
+        appView
+            .appInfoForDesugaring()
+            .resolveMethod(invokedMethod, interfaceBit)
+            .asSingleResolution();
+    if (resolution != null
+        && (resolution.getResolvedHolder().isLibraryClass()
+            || helper.isEmulatedInterface(resolution.getResolvedHolder().type))) {
+      DexClassAndMethod defaultMethod =
+          appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
+      if (defaultMethod != null && !helper.dontRewrite(defaultMethod)) {
+        assert !defaultMethod.getAccessFlags().isAbstract();
+        return defaultMethod;
+      }
+    }
+    return null;
   }
 
   private boolean shouldRewriteToInvokeToThrow(
@@ -771,6 +809,40 @@
         || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
   }
 
+  private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
+    // Here we try to avoid doing the expensive look-up on all invokes.
+    if (!emulatedMethods.contains(invokedMethod.name)) {
+      return null;
+    }
+    DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+    // We cannot rewrite the invoke we do not know what the class is.
+    if (dexClass == null) {
+      return null;
+    }
+    DexEncodedMethod singleTarget = null;
+    if (dexClass.isInterface()) {
+      // Look for exact method on the interface.
+      singleTarget = dexClass.lookupMethod(invokedMethod);
+    }
+    if (singleTarget == null) {
+      DexClassAndMethod result =
+          appView.appInfoForDesugaring().lookupMaximallySpecificMethod(dexClass, invokedMethod);
+      if (result != null) {
+        singleTarget = result.getDefinition();
+      }
+    }
+    if (singleTarget == null) {
+      // At this point we are in a library class. Failures can happen with NoSuchMethod if a
+      // library class implement a method with same signature but not related to emulated
+      // interfaces.
+      return null;
+    }
+    if (!singleTarget.isAbstract() && helper.isEmulatedInterface(singleTarget.getHolderType())) {
+      return singleTarget.getHolderType();
+    }
+    return null;
+  }
+
   private boolean isNonDesugaredLibraryClass(DexClass clazz) {
     return clazz.isLibraryClass() && !helper.isInDesugaredLibrary(clazz);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 7e8a2cc..f712cb4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -5,9 +5,11 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -23,16 +25,27 @@
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Iterables;
+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.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public final class ProgramEmulatedInterfaceSynthesizer implements CfClassSynthesizerDesugaring {
 
   private final AppView<?> appView;
   private final InterfaceDesugaringSyntheticHelper helper;
+  private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
 
   public static ProgramEmulatedInterfaceSynthesizer create(AppView<?> appView) {
     if (!appView.options().isDesugaredLibraryCompilation()
-        || !appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces()) {
+        || appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()) {
       return null;
     }
     return new ProgramEmulatedInterfaceSynthesizer(appView);
@@ -41,11 +54,53 @@
   public ProgramEmulatedInterfaceSynthesizer(AppView<?> appView) {
     this.appView = appView;
     helper = new InterfaceDesugaringSyntheticHelper(appView);
+    // Avoid the computation outside L8 since it is not needed.
+    emulatedInterfacesHierarchy =
+        appView.options().isDesugaredLibraryCompilation()
+            ? processEmulatedInterfaceHierarchy()
+            : Collections.emptyMap();
+  }
+
+  private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy() {
+    Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
+    Set<DexType> processed = Sets.newIdentityHashSet();
+    ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(helper.getEmulatedInterfaces());
+    emulatedInterfacesSorted.sort(DexType::compareTo);
+    for (DexType interfaceType : emulatedInterfacesSorted) {
+      processEmulatedInterfaceHierarchy(interfaceType, processed, emulatedInterfacesHierarchy);
+    }
+    return emulatedInterfacesHierarchy;
+  }
+
+  private void processEmulatedInterfaceHierarchy(
+      DexType interfaceType,
+      Set<DexType> processed,
+      Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
+    if (processed.contains(interfaceType)) {
+      return;
+    }
+    emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
+    processed.add(interfaceType);
+    DexClass theInterface = appView.definitionFor(interfaceType);
+    if (theInterface == null) {
+      return;
+    }
+    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(theInterface.interfaces.values));
+    while (!workList.isEmpty()) {
+      DexType next = workList.removeLast();
+      if (helper.isEmulatedInterface(next)) {
+        processEmulatedInterfaceHierarchy(next, processed, emulatedInterfacesHierarchy);
+        emulatedInterfacesHierarchy.get(next).add(interfaceType);
+        DexClass nextClass = appView.definitionFor(next);
+        if (nextClass != null) {
+          workList.addAll(Arrays.asList(nextClass.interfaces.values));
+        }
+      }
+    }
   }
 
   DexProgramClass synthesizeProgramEmulatedInterface(
       DexProgramClass emulatedInterface,
-      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
       L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     return appView
         .getSyntheticItems()
@@ -53,26 +108,20 @@
             SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
             emulatedInterface,
             appView,
-            builder ->
-                synthesizeEmulateInterfaceMethods(
-                    emulatedInterface, emulatedInterfaceDescriptor, builder),
+            builder -> synthesizeEmulateInterfaceMethods(emulatedInterface, builder),
             eventConsumer::acceptProgramEmulatedInterface);
   }
 
   private void synthesizeEmulateInterfaceMethods(
-      DexProgramClass emulatedInterface,
-      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
-      SyntheticProgramClassBuilder builder) {
+      DexProgramClass emulatedInterface, SyntheticProgramClassBuilder builder) {
+    assert helper.isEmulatedInterface(emulatedInterface.type);
     emulatedInterface.forEachProgramVirtualMethodMatching(
-        m -> emulatedInterfaceDescriptor.getEmulatedMethods().containsKey(m.getReference()),
+        DexEncodedMethod::isDefaultMethod,
         method ->
             builder.addMethod(
                 methodBuilder ->
                     synthesizeEmulatedInterfaceMethod(
-                        method,
-                        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference()),
-                        builder.getType(),
-                        methodBuilder)));
+                        method, emulatedInterface, builder.getType(), methodBuilder)));
   }
 
   private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
@@ -88,17 +137,24 @@
 
   private void synthesizeEmulatedInterfaceMethod(
       ProgramMethod method,
-      EmulatedDispatchMethodDescriptor descriptor,
+      DexProgramClass theInterface,
       DexType dispatchType,
       SyntheticMethodBuilder methodBuilder) {
     assert !method.getDefinition().isStatic();
-    DexMethod emulatedMethod =
-        helper.emulatedInterfaceDispatchMethod(
-            descriptor.getEmulatedDispatchMethod(), dispatchType);
-    DexMethod itfMethod = helper.emulatedInterfaceInterfaceMethod(descriptor.getInterfaceMethod());
+    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+      synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+          method, theInterface, dispatchType, methodBuilder);
+      return;
+    }
+    DexMethod emulatedMethod = helper.emulateInterfaceLibraryMethod(method);
+    DexMethod itfMethod =
+        method
+            .getReference()
+            .withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
     DexMethod companionMethod =
-        helper.ensureEmulatedInterfaceForwardingMethod(descriptor.getForwardingMethod());
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
+        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases =
+        getDispatchCases(method, theInterface, companionMethod);
     methodBuilder
         .setName(emulatedMethod.getName())
         .setProto(emulatedMethod.getProto())
@@ -114,40 +170,162 @@
                     .generateCfCode());
   }
 
+  private void synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+      ProgramMethod method,
+      DexProgramClass theInterface,
+      DexType dispatchType,
+      SyntheticMethodBuilder methodBuilder) {
+    EmulatedInterfaceDescriptor emulatedInterfaceDescriptor =
+        appView
+            .options()
+            .testing
+            .machineDesugaredLibrarySpecification
+            .getRewritingFlags()
+            .getEmulatedInterfaces()
+            .get(theInterface.type);
+    EmulatedDispatchMethodDescriptor descriptor =
+        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference());
+    DexMethod emulatedMethod = emulatedMethod(descriptor.getEmulatedDispatchMethod(), dispatchType);
+    DexMethod itfMethod = interfaceMethod(descriptor.getInterfaceMethod());
+    // TODO(b/184026720): Adapt to use the forwarding method.
+    DerivedMethod forwardingMethod = descriptor.getForwardingMethod();
+    assert forwardingMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+    assert forwardingMethod.getMethod() == method.getReference();
+    DexMethod companionMethod =
+        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
+    methodBuilder
+        .setName(descriptor.getEmulatedDispatchMethod().getName())
+        .setProto(descriptor.getEmulatedDispatchMethod().getProto())
+        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+        .setCode(
+            emulatedInterfaceMethod ->
+                new EmulateDispatchSyntheticCfCodeProvider(
+                        emulatedMethod.getHolderType(),
+                        companionMethod,
+                        itfMethod,
+                        extraDispatchCases,
+                        appView)
+                    .generateCfCode());
+  }
+
   private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
       EmulatedDispatchMethodDescriptor descriptor) {
     LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
     descriptor
         .getDispatchCases()
         .forEach(
-            (type, derivedMethod) ->
-                extraDispatchCases.put(
-                    type, helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod)));
+            (type, derivedMethod) -> {
+              DexMethod caseMethod;
+              if (derivedMethod.getHolderKind() == null) {
+                caseMethod = derivedMethod.getMethod();
+              } else {
+                assert derivedMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+                ProgramMethod resolvedProgramMethod =
+                    appView
+                        .appInfoForDesugaring()
+                        .resolveMethod(derivedMethod.getMethod(), true)
+                        .getResolvedProgramMethod();
+                caseMethod =
+                    helper
+                        .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod)
+                        .getReference();
+              }
+              extraDispatchCases.put(type, caseMethod);
+            });
     return extraDispatchCases;
   }
 
+  private LinkedHashMap<DexType, DexMethod> getDispatchCases(
+      ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
+    // To properly emulate the library interface call, we need to compute the interfaces
+    // inheriting from the interface and manually implement the dispatch with instance of.
+    // The list guarantees that an interface is always after interfaces it extends,
+    // hence reverse iteration.
+    List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
+    // In practice, there is usually a single case (except for tests),
+    // so we do not bother to make the following loop more clever.
+    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
+    for (DexString methodName : retargetCoreLibMember.keySet()) {
+      if (method.getName() == methodName) {
+        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+          DexClass inClass = appView.definitionFor(inType);
+          if (inClass != null && implementsInterface(inClass, theInterface.type)) {
+            extraDispatchCases.put(
+                inType,
+                appView
+                    .dexItemFactory()
+                    .createMethod(
+                        retargetCoreLibMember.get(methodName).get(inType),
+                        appView
+                            .dexItemFactory()
+                            .protoWithDifferentFirstParameter(companionMethod.proto, inType),
+                        method.getName()));
+          }
+        }
+      }
+    }
+    if (subInterfaces != null) {
+      for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+        DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
+        assert subInterfaceClass != null;
+        assert subInterfaceClass.isProgramClass();
+        // Else computation of subInterface would have failed.
+        // if the method is implemented, extra dispatch is required.
+        DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
+        if (result != null && !result.isAbstract()) {
+          assert result.isDefaultMethod();
+          DexMethod forward =
+              helper
+                  .ensureDefaultAsMethodOfProgramCompanionClassStub(
+                      new ProgramMethod(subInterfaceClass.asProgramClass(), result))
+                  .getReference();
+          extraDispatchCases.put(subInterfaceClass.type, forward);
+        }
+      }
+    } else {
+      assert extraDispatchCases.size() <= 1;
+    }
+    return extraDispatchCases;
+  }
+
+  private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
+    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(clazz.interfaces.values));
+    while (!workList.isEmpty()) {
+      DexType next = workList.removeLast();
+      if (interfaceType == next) {
+        return true;
+      }
+      DexClass nextClass = appView.definitionFor(next);
+      if (nextClass != null) {
+        workList.addAll(Arrays.asList(nextClass.interfaces.values));
+      }
+    }
+    return false;
+  }
+
   @Override
   public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     assert appView.options().isDesugaredLibraryCompilation();
-    appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .getEmulatedInterfaces()
-        .forEach(
-            (emulatedInterfaceType, emulatedInterfaceDescriptor) -> {
-              DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
-              if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
-                warnMissingEmulatedInterface(emulatedInterfaceType);
-                return;
-              }
-              DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
-              assert emulatedInterface != null;
-              if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
-                  && !emulatedInterfaceDescriptor.getEmulatedMethods().isEmpty()) {
-                synthesizeProgramEmulatedInterface(
-                    emulatedInterface, emulatedInterfaceDescriptor, eventConsumer);
-              }
-            });
+    for (DexType emulatedInterfaceType : helper.getEmulatedInterfaces()) {
+      DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
+      if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
+        warnMissingEmulatedInterface(emulatedInterfaceType);
+        continue;
+      }
+      DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
+      assert emulatedInterface != null;
+      if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
+          && needsEmulateInterfaceLibrary(emulatedInterface)) {
+        synthesizeProgramEmulatedInterface(emulatedInterface, eventConsumer);
+      }
+    }
+  }
+
+  private boolean needsEmulateInterfaceLibrary(DexClass emulatedInterface) {
+    return Iterables.any(emulatedInterface.methods(), DexEncodedMethod::isDefaultMethod);
   }
 
   private void warnMissingEmulatedInterface(DexType interfaceType) {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 0c1e782..e156407 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -665,6 +665,7 @@
             || getMissingClasses().contains(type)
             // TODO(b/150693139): Remove these exceptions once fixed.
             || InterfaceDesugaringSyntheticHelper.isCompanionClassType(type)
+            || InterfaceDesugaringSyntheticHelper.isEmulatedLibraryClassType(type)
             // TODO(b/150736225): Not sure how to remove these.
             || DesugaredLibraryAPIConverter.isVivifiedType(type)
         : "Failed lookup of non-missing type: " + type;
diff --git a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
index ab0a04d..960ccd5 100644
--- a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
@@ -28,9 +28,9 @@
 
   public L8TreePruner(InternalOptions options) {
     this.options = options;
-    backports.addAll(options.machineDesugaredLibrarySpecification.getLegacyBackport().keySet());
+    backports.addAll(options.desugaredLibrarySpecification.getBackportCoreLibraryMember().keySet());
     emulatedInterfaces.addAll(
-        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet());
+        options.desugaredLibrarySpecification.getEmulateLibraryInterface().keySet());
   }
 
   public DexApplication prune(DexApplication app, PrefixRewritingMapper rewritePrefix) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 604748a..bbf4258 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -129,7 +129,12 @@
       return;
     }
     assert hygienicType.toSourceString().startsWith(synthesizingContextType.toSourceString());
-    DexType rewrittenContext = appView.rewritePrefix.rewrittenContextType(synthesizingContextType);
+    DexType rewrittenContext =
+        appView
+            .options()
+            .desugaredLibrarySpecification
+            .getEmulateLibraryInterface()
+            .get(synthesizingContextType);
     if (rewrittenContext == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 6d45ee1..0cb0137 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -483,7 +483,8 @@
       AppView<?> appView,
       DexType type) {
     DexType rewrittenContextType =
-        appView.rewritePrefix.rewrittenContextType(outerContext.getSynthesizingContextType());
+        appView.rewritePrefix.rewrittenContextType(
+            outerContext.getSynthesizingContextType(), appView);
     if (rewrittenContextType == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 467ac25..ec5dca6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -54,11 +54,8 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.MachineDesugarPrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -88,9 +85,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import java.io.IOException;
 import java.io.PrintStream;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -395,8 +390,8 @@
     if (isGeneratingDex() || desugarState == DesugarState.ON) {
       marker.setMinApi(getMinApiLevel().getLevel());
     }
-    if (machineDesugaredLibrarySpecification.getIdentifier() != null) {
-      marker.setDesugaredLibraryIdentifiers(machineDesugaredLibrarySpecification.getIdentifier());
+    if (desugaredLibrarySpecification.getIdentifier() != null) {
+      marker.setDesugaredLibraryIdentifiers(desugaredLibrarySpecification.getIdentifier());
     }
     if (Version.isDevelopmentVersion()) {
       marker.setSha1(VersionProperties.INSTANCE.getSha());
@@ -443,7 +438,7 @@
   }
 
   public boolean isDesugaredLibraryCompilation() {
-    return machineDesugaredLibrarySpecification.isLibraryCompilation();
+    return desugaredLibrarySpecification.isLibraryCompilation();
   }
 
   public boolean isRelocatorCompilation() {
@@ -893,51 +888,13 @@
   public LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
       LegacyDesugaredLibrarySpecification.empty();
 
-  public void setDesugaredLibrarySpecification(
-      LegacyDesugaredLibrarySpecification specification, AndroidApp app) {
-    if (specification.isEmptyConfiguration()) {
-      return;
-    }
-    desugaredLibrarySpecification = specification;
-    try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(specification, app.getLibraryResourceProviders(), this);
-      machineDesugaredLibrarySpecification =
-          new HumanToMachineSpecificationConverter()
-              .convert(
-                  human,
-                  specification.isLibraryCompilation() ? app.getProgramResourceProviders() : null,
-                  app.getLibraryResourceProviders(),
-                  this);
-    } catch (IOException e) {
-      reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
-    }
-  }
-
-  public void setDesugaredLibrarySpecificationForTesting(
-      LegacyDesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
-      throws IOException {
-    desugaredLibrarySpecification = specification;
-    HumanDesugaredLibrarySpecification human =
-        new LegacyToHumanSpecificationConverter().convert(specification, library, this);
-    machineDesugaredLibrarySpecification =
-        new HumanToMachineSpecificationConverter()
-            .convert(
-                human,
-                specification.isLibraryCompilation() ? desugaredJDKLib : null,
-                library,
-                this);
-  }
-
-  // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
-  public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
-      MachineDesugaredLibrarySpecification.empty();
-
   public PrefixRewritingMapper getPrefixRewritingMapper() {
-    return machineDesugaredLibrarySpecification.getRewriteType().isEmpty()
-        ? PrefixRewritingMapper.empty()
-        : new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification);
+    if (testing.machineDesugaredLibrarySpecification != null) {
+      return new MachineDesugarPrefixRewritingMapper(
+          desugaredLibrarySpecification.getPrefixRewritingMapper(),
+          testing.machineDesugaredLibrarySpecification.getRewritingFlags());
+    }
+    return desugaredLibrarySpecification.getPrefixRewritingMapper();
   }
 
   public boolean relocatorCompilation = false;
@@ -1642,6 +1599,9 @@
 
     public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {};
 
+    // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
+    public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification = null;
+
     /**
      * If this flag is enabled, we will also compute the set of possible targets for invoke-
      * interface and invoke-virtual instructions that target a library method, and add the
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index 6ed9d46..706126c 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -155,8 +155,8 @@
               .setConsumer(new ListStringConsumer())
               .build());
       fail("Expected failure");
-    } catch (Throwable e) {
-      // This should throw a CompilationFailedException but an assertion is failing first.
+    } catch (CompilationFailedException e) {
+      // Expected.
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index cf095b1..9740064 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -632,18 +632,9 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    D8Command d8Command =
-        parse(
-            "--desugared-lib",
-            "src/library_desugar/desugar_jdk_libs.json",
-            "--lib",
-            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
+    D8Command d8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
     assertFalse(
-        d8Command
-            .getInternalOptions()
-            .machineDesugaredLibrarySpecification
-            .getRewriteType()
-            .isEmpty());
+        d8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 358d863..e1902f4 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -355,17 +355,9 @@
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
     L8Command l8Command =
-        parse(
-            "--desugared-lib",
-            ToolHelper.getDesugarLibJsonForTesting().toString(),
-            "--lib",
-            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
+        parse("--desugared-lib", ToolHelper.getDesugarLibJsonForTesting().toString());
     assertFalse(
-        l8Command
-            .getInternalOptions()
-            .machineDesugaredLibrarySpecification
-            .getRewriteType()
-            .isEmpty());
+        l8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
   }
 
   private void checkSingleForceAllAssertion(
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 7a42ab1..44202c6 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -108,10 +108,6 @@
     return this;
   }
 
-  public TestDiagnosticMessages getDiagnosticMessages() {
-    return state.getDiagnosticsMessages();
-  }
-
   public L8TestBuilder setDebug() {
     this.mode = CompilationMode.DEBUG;
     return this;
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 325344f..0b48fc2 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -763,18 +763,9 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    R8Command r8Command =
-        parse(
-            "--desugared-lib",
-            "src/library_desugar/desugar_jdk_libs.json",
-            "--lib",
-            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
+    R8Command r8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
     assertFalse(
-        r8Command
-            .getInternalOptions()
-            .machineDesugaredLibrarySpecification
-            .getRewriteType()
-            .isEmpty());
+        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
   }
 
   @Test
@@ -784,16 +775,10 @@
         parse(
             "--desugared-lib",
             "src/library_desugar/desugar_jdk_libs.json",
-            "--lib",
-            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
             "--desugared-lib-pg-conf-output",
             pgout.toString());
     assertFalse(
-        r8Command
-            .getInternalOptions()
-            .machineDesugaredLibrarySpecification
-            .getRewriteType()
-            .isEmpty());
+        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
index c8f932d..72cdc59 100644
--- a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
@@ -3,15 +3,23 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.LibraryDesugaringTestConfiguration;
-import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.List;
 import java.util.function.Consumer;
@@ -25,15 +33,19 @@
 
   private static final String EXPECTED = StringUtils.lines("Y", "89");
 
-  @Parameterized.Parameters(name = "{0}")
+  @Parameterized.Parameters(name = "{0}, old-rt:{1}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
   }
 
   private final TestParameters parameters;
+  private final boolean rtWithoutConsumer;
 
-  public InvokeSuperToRewrittenDefaultMethodTest(TestParameters parameters) {
+  public InvokeSuperToRewrittenDefaultMethodTest(
+      TestParameters parameters, boolean rtWithoutConsumer) {
     this.parameters = parameters;
+    this.rtWithoutConsumer = rtWithoutConsumer;
   }
 
   private boolean needsDefaultInterfaceMethodDesugaring() {
@@ -53,15 +65,35 @@
   @Test
   public void testDesugaring() throws Exception {
     assumeTrue(needsDefaultInterfaceMethodDesugaring());
-    testForD8()
-        .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
-        .setMinApi(parameters.getApiLevel())
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-        .enableCoreLibraryDesugaring(
-            LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
-        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
-        .run(parameters.getRuntime(), TestClass.class)
-        .assertSuccessWithOutput(EXPECTED);
+    try {
+      testForD8()
+          .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
+          .setMinApi(parameters.getApiLevel())
+          .addLibraryFiles(
+              rtWithoutConsumer
+                  ? ToolHelper.getAndroidJar(AndroidApiLevel.B)
+                  : ToolHelper.getAndroidJar(AndroidApiLevel.P))
+          .enableCoreLibraryDesugaring(
+              LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                if (rtWithoutConsumer) {
+                  diagnostics.assertOnlyErrors();
+                  // TODO(b/158543011): Should fail with a nice user error for invalid library.
+                  diagnostics.assertErrorsMatch(
+                      allOf(
+                          diagnosticType(ExceptionDiagnostic.class),
+                          diagnosticMessage(containsString("AssertionError"))));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              })
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+      assertFalse(rtWithoutConsumer);
+    } catch (CompilationFailedException e) {
+      assertTrue(rtWithoutConsumer);
+    }
   }
 
   public interface CharConsumer extends Consumer<Character>, IntConsumer {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index b5e5c03..6b54606 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -73,13 +73,11 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    setDesugaredLibrarySpecificationForTesting(
-        options, configurationAlternative3(options, false, parameters));
+    options.desugaredLibrarySpecification = configurationAlternative3(options, false, parameters);
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    setDesugaredLibrarySpecificationForTesting(
-        options, configurationAlternative3(options, true, parameters));
+    options.desugaredLibrarySpecification = configurationAlternative3(options, true, parameters);
   }
 
   @Test
@@ -143,7 +141,10 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(this::configurationForProgramCompilation)
+        .addOptionsModification(
+            options ->
+                options.desugaredLibrarySpecification =
+                    configurationAlternative3(options, false, parameters))
         .addInnerClasses(BufferedReaderTest.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -169,7 +170,10 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForR8(parameters.getBackend())
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(this::configurationForProgramCompilation)
+        .addOptionsModification(
+            options ->
+                options.desugaredLibrarySpecification =
+                    configurationAlternative3(options, false, parameters))
         .addInnerClasses(BufferedReaderTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index 473153c..d1e9746 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -10,11 +10,17 @@
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -34,15 +40,19 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
+  private final boolean machineSpec;
 
-  @Parameters(name = "machine: {0}, shrink: {1}")
+  @Parameters(name = "machine: {0}, {2}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
+        BooleanUtils.values(),
         getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
-  public CustomCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+  public CustomCollectionTest(
+      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.machineSpec = machineSpec;
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -50,6 +60,22 @@
   private final String EXECUTOR =
       "com.android.tools.r8.desugar.desugaredlibrary.CustomCollectionTest$Executor";
 
+  private void setMachineSpec(InternalOptions opt) {
+    if (!machineSpec) {
+      return;
+    }
+    try {
+      HumanDesugaredLibrarySpecification human =
+          new LegacyToHumanSpecificationConverter()
+              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
+      MachineDesugaredLibrarySpecification machine =
+          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
+      opt.testing.machineDesugaredLibrarySpecification = machine;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   @Test
   public void testCustomCollectionD8() throws Exception {
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
@@ -57,6 +83,7 @@
         testForD8()
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
+            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -79,6 +106,7 @@
     Path jar =
         testForD8(Backend.CF)
             .addInnerClasses(CustomCollectionTest.class)
+            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -134,6 +162,7 @@
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
+            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .addKeepClassAndMembersRules(Executor.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
index e27dda9..3e7d97d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
@@ -43,9 +43,10 @@
             "",
             false,
             Collections.emptyList(),
-            options ->
-                setDesugaredLibrarySpecificationForTesting(
-                    options, chmOnlyConfiguration(options, true, parameters)));
+            options -> {
+              options.desugaredLibrarySpecification =
+                  chmOnlyConfiguration(options, true, parameters);
+            });
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
@@ -60,9 +61,10 @@
             "-keep class * { *; }",
             true,
             Collections.emptyList(),
-            options ->
-                setDesugaredLibrarySpecificationForTesting(
-                    options, chmOnlyConfiguration(options, true, parameters)));
+            options -> {
+              options.desugaredLibrarySpecification =
+                  chmOnlyConfiguration(options, true, parameters);
+            });
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index 819aa45..e353126 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -5,19 +5,15 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
 
 import com.android.tools.r8.L8Command;
-import com.android.tools.r8.L8TestBuilder;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -29,7 +25,6 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Collections;
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,33 +45,6 @@
   }
 
   @Test
-  public void testInvalidLibrary() throws Exception {
-    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
-    L8TestBuilder l8TestBuilder =
-        testForL8(parameters.getApiLevel())
-            .addProgramFiles(Collections.singleton(ToolHelper.getDesugarJDKLibs()))
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.L))
-            .setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS);
-    try {
-      l8TestBuilder.compile();
-      fail();
-    } catch (AssertionError ae) {
-      // Expected since the library is invalid.
-    }
-    TestDiagnosticMessages diagnosticMessages = l8TestBuilder.getDiagnosticMessages();
-    diagnosticMessages.assertOnlyWarnings();
-    assertEquals(diagnosticMessages.getWarnings().size(), 1);
-    assertTrue(
-        diagnosticMessages
-            .getWarnings()
-            .get(0)
-            .getDiagnosticMessage()
-            .contains(
-                "Desugared library requires to be compiled with a library file of API greater or"
-                    + " equal to"));
-  }
-
-  @Test
   public void testDesugaredLibraryContentD8() throws Exception {
     Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     CodeInspector inspector = new CodeInspector(buildDesugaredLibrary(parameters.getApiLevel()));
@@ -121,16 +89,11 @@
     ToolHelper.runL8(l8Builder.build(), options -> {});
     CodeInspector codeInspector = new CodeInspector(desugaredLib);
     assertCorrect(codeInspector);
-    assertOneWarning(diagnosticsHandler);
+    assertNoWarningsErrors(diagnosticsHandler);
   }
 
-  private void assertOneWarning(TestDiagnosticMessagesImpl diagnosticsHandler) {
-    assertEquals(1, diagnosticsHandler.getWarnings().size());
-    String msg = diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage();
-    assertTrue(
-        msg.contains(
-            "The following library types, prefixed by java., are present both as library and non"
-                + " library classes"));
+  private void assertNoWarningsErrors(TestDiagnosticMessagesImpl diagnosticsHandler) {
+    assertTrue(diagnosticsHandler.getWarnings().isEmpty());
     assertTrue(diagnosticsHandler.getErrors().isEmpty());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index a3832c1..34faa58 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -67,18 +67,6 @@
     return property.contains("jdk11");
   }
 
-  public void setDesugaredLibrarySpecificationForTesting(
-      InternalOptions options, LegacyDesugaredLibrarySpecification specification) {
-    try {
-      options.setDesugaredLibrarySpecificationForTesting(
-          specification,
-          ToolHelper.getDesugarJDKLibs(),
-          ToolHelper.getAndroidJar(AndroidApiLevel.R));
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   // For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
   // above N and O depending if Stream or Time APIs are used), but we need to compile the program
   // with a minAPI below to force the use of conversions.
@@ -101,7 +89,7 @@
 
   protected Path getLibraryFile() {
     return isJDK11DesugaredLibrary()
-        ? ToolHelper.getAndroidJar(AndroidApiLevel.R)
+        ? ToolHelper.getAndroidJar(AndroidApiLevel.S)
         : ToolHelper.getAndroidJar(AndroidApiLevel.P);
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
index e1a8586..0c61a06 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
@@ -68,15 +65,6 @@
           Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
     }
     ToolHelper.runL8(l8Builder.build(), options -> {});
-    assertEquals(diagnosticsHandler.getWarnings().size(), 1);
-    diagnosticsHandler.assertNoErrors();
-    assertTrue(
-        diagnosticsHandler
-            .getWarnings()
-            .get(0)
-            .getDiagnosticMessage()
-            .contains(
-                "The following library types, prefixed by java., are present both as library and"
-                    + " non library classes:"));
+    diagnosticsHandler.assertNoMessages();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
new file mode 100644
index 0000000..b1f57e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, 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.desugar.desugaredlibrary;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InconsistentPrefixTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public InconsistentPrefixTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testNoInconsistentPrefixes() throws Exception {
+    Map<String, String> x = new HashMap<>();
+    x.put("pkg.sub", "p$.bus");
+    x.put("pkg", "p$");
+
+    JasminBuilder jasminBuilder = new JasminBuilder();
+    jasminBuilder.addClass("pkg/notsub/A");
+    jasminBuilder.addClass("pkg/sub/A");
+    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+    jasminBuilder.writeJar(inputJar);
+
+    testForD8()
+        .addProgramFiles(inputJar)
+        .addOptionsModification(
+            options ->
+                options.desugaredLibrarySpecification =
+                    LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(x, options))
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertErrorMessageThatMatches(
+                  containsString("Inconsistent prefix in desugared library"));
+            });
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
index 371443d..88f3ca9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
@@ -5,12 +5,15 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
 
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.BooleanUtils;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -28,6 +31,7 @@
 public class J$ExtensionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
 
   private static final String MAIN_CLASS_NAME = "Main";
   private static final String MAIN_CLASS =
@@ -51,12 +55,14 @@
           + "}";
   private static Path[] compiledClasses = new Path[2];
 
-  @Parameters(name = "{0}")
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withAllRuntimes().withAllApiLevels().build());
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimes().withAllApiLevels().build());
   }
 
-  public J$ExtensionTest(TestParameters parameters) {
+  public J$ExtensionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
 
@@ -86,6 +92,7 @@
 
   @Test
   public void testJ$ExtensionNoDesugaring() throws Exception {
+    Assume.assumeFalse(shrinkDesugaredLibrary);
     String stderr;
     if (parameters.isCfRuntime()) {
       stderr =
@@ -103,31 +110,24 @@
               .assertFailure()
               .getStdErr();
     }
-    assertError(stderr, false);
+    assertError(stderr);
   }
 
-  private void assertError(String stderr, boolean desugaring) {
-    if (parameters.isCfRuntime()) {
-      if (parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
-        assertTrue(
-            stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
-      } else {
-        assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
-      }
-      return;
-    }
-    assert !parameters.isCfRuntime();
-    if (!desugaring) {
-      if (parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V6_0_1)) {
-        assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
-      } else if (parameters.getDexRuntimeVersion() == Version.V7_0_0) {
-        assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
-      }
-      return;
-    }
-    if (parameters.getDexRuntimeVersion() == Version.V8_1_0) {
-      // On Android 8 the library package private method is accessible.
-      assertTrue(stderr.contains("java.lang.NullPointerException"));
+  private void assertError(String stderr) {
+    if (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
+      assertTrue(
+          stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
+    } else if (parameters.isCfRuntime()) {
+      assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
+    } else if (parameters
+        .getRuntime()
+        .asDex()
+        .getVm()
+        .getVersion()
+        .isOlderThanOrEqual(Version.V6_0_1)) {
+      assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
+    } else if (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
+      assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
     } else {
       assertTrue(stderr.contains("java.lang.IllegalAccessError"));
     }
@@ -140,18 +140,19 @@
     Assume.assumeTrue(requiresTimeDesugaring(parameters));
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
 
-    String stdErr =
-        testForD8()
-            .addLibraryFiles(getLibraryFile())
-            .addProgramFiles(compiledClasses)
-            .setMinApi(parameters.getApiLevel())
-            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
-            .compile()
-            .addDesugaredCoreLibraryRunClassPath(
-                this::buildDesugaredLibrary, parameters.getApiLevel())
-            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
-            .assertFailure()
-            .getStdErr();
-    assertError(stdErr, true);
+    try {
+      testForD8()
+          .addLibraryFiles(getLibraryFile())
+          .addProgramFiles(compiledClasses)
+          .setMinApi(parameters.getApiLevel())
+          .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+          .compile();
+      fail();
+    } catch (CompilationFailedException e) {
+      assertTrue(
+          e.getCause()
+              .getMessage()
+              .contains("Cannot compile program class java.time.LocalTimeAccess"));
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index d78df31..6598d6b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -144,22 +144,16 @@
             .map(TypeSubject::toString)
             .collect(Collectors.toSet());
     assertEquals(expectedCatchGuards, foundCatchGuards);
-    if (!(parameters
-            .getApiLevel()
-            .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
-        && isR8)) {
+
+    if (isR8) {
+      assertThat(
+          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
+          not(isPresent()));
+    } else {
       assertThat(
           inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
-              null, inspector.clazz(TemporalAccessorImpl.class).getFinalName(), "query", null));
-    } else {
-      String holder =
-          requiresTimeDesugaring(parameters)
-              ? "j$.time.temporal.TemporalAccessor"
-              : "java.time.temporal.TemporalAccessor";
-      assertThat(
-          inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
-          CodeMatchers.invokesMethod(null, holder, "query", null));
+              null, TemporalAccessorImpl.class.getTypeName(), "query", null));
     }
     if (parameters
         .getApiLevel()
@@ -169,7 +163,9 @@
           not(isPresent()));
     } else {
       assertThat(
-          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
+          inspector
+              .clazz(isR8 ? TemporalAccessorImplSub.class : TemporalAccessorImpl.class)
+              .uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
               null, "j$.time.temporal.TemporalAccessor$-CC", "$default$query", null));
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
index de87214..981edea 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
@@ -11,7 +11,6 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
-import java.util.function.Predicate;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -166,14 +165,6 @@
       System.out.println("true");
       System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
       System.out.println("false");
-
-      spliterator = ((Set<String>) new CustomLinkedHashSetForceForwarding<String>()).spliterator();
-      System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
-      System.out.println("true");
-      System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
-      System.out.println("true");
-      System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
-      System.out.println("false");
     }
   }
 
@@ -207,13 +198,4 @@
       return super.spliterator();
     }
   }
-
-  // The over method force the forwarding methods to be installed.
-  static class CustomLinkedHashSetForceForwarding<E> extends LinkedHashSet<E> {
-
-    @Override
-    public boolean removeIf(Predicate<? super E> filter) {
-      return super.removeIf(filter);
-    }
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
index 1208414..d5300ad 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -118,8 +118,8 @@
     L8.run(
         L8Command.builder()
             .addLibraryFiles(getLibraryFile())
-            .addLibraryFiles(ToolHelper.getDesugarJDKLibs())
             .addProgramFiles(JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES)
+            .addClasspathFiles(ToolHelper.getDesugarJDKLibs())
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index f3caf57..abdf5c5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -99,13 +99,13 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    setDesugaredLibrarySpecificationForTesting(
-        options, desugaredLibrarySpecification(options, false, parameters));
+    options.desugaredLibrarySpecification =
+        desugaredLibrarySpecification(options, false, parameters);
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    setDesugaredLibrarySpecificationForTesting(
-        options, desugaredLibrarySpecification(options, true, parameters));
+    options.desugaredLibrarySpecification =
+        desugaredLibrarySpecification(options, true, parameters);
   }
 
   private Matcher<MethodSubject> invokesObjectsCompare(String holder) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
index 9b1804e..359a18c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyTopLevelFlags;
@@ -15,7 +14,6 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import java.io.IOException;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,15 +55,9 @@
             .putBackportCoreLibraryMember("java.lang.DesugarMath", "java.lang.Math")
             .putRetargetCoreLibMember("java.util.Date#toInstant", "java.util.DesugarDate")
             .build();
-    try {
-      options.setDesugaredLibrarySpecificationForTesting(
-          new LegacyDesugaredLibrarySpecification(
-              LegacyTopLevelFlags.testing(), rewritingFlags, true),
-          ToolHelper.getDesugarJDKLibs(),
-          ToolHelper.getAndroidJar(AndroidApiLevel.R));
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+    options.desugaredLibrarySpecification =
+        new LegacyDesugaredLibrarySpecification(
+            LegacyTopLevelFlags.testing(), rewritingFlags, true, options.itemFactory);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index e629c47..6bf49a0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,7 +5,13 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.time.ZonedDateTime;
@@ -25,15 +31,19 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
+  private final boolean machineSpec;
 
   @Parameters(name = "machine: {0}, {2}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
+        BooleanUtils.values(),
         getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
   }
 
-  public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+  public RetargetOverrideTest(
+      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.machineSpec = machineSpec;
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -44,6 +54,7 @@
     Path desugaredTwice =
         testForD8(Backend.CF)
             .addLibraryFiles(getLibraryFile())
+            .addOptionsModification(this::setMachineSpec)
             .addProgramFiles(
                 testForD8(Backend.CF)
                     .addLibraryFiles(getLibraryFile())
@@ -65,6 +76,7 @@
       stdout =
           testForD8(Backend.DEX)
               .addProgramFiles(desugaredTwice)
+              .addOptionsModification(this::setMachineSpec)
               .setMinApi(parameters.getApiLevel())
               .disableDesugaring()
               .compile()
@@ -95,6 +107,22 @@
     assertLines2By2Correct(stdout);
   }
 
+  private void setMachineSpec(InternalOptions opt) {
+    if (!machineSpec) {
+      return;
+    }
+    try {
+      HumanDesugaredLibrarySpecification human =
+          new LegacyToHumanSpecificationConverter()
+              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
+      MachineDesugaredLibrarySpecification machine =
+          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
+      opt.testing.machineDesugaredLibrarySpecification = machine;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   @Test
   public void testRetargetOverrideD8() throws Exception {
     Assume.assumeTrue(parameters.getRuntime().isDex());
@@ -102,6 +130,7 @@
     String stdout =
         testForD8()
             .addLibraryFiles(getLibraryFile())
+            .addOptionsModification(this::setMachineSpec)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .setMinApi(parameters.getApiLevel())
@@ -127,6 +156,7 @@
     String stdout =
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
+            .addOptionsModification(this::setMachineSpec)
             .addKeepMainRule(Executor.class)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
index 7abed46..f9d1828 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -58,7 +58,6 @@
         .setMinApi(AndroidApiLevel.B)
         .addProgramClasses(Executor.class)
         .addLibraryClasses(CustomLibClass.class)
-        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
index a57140b..6691cb1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
@@ -66,8 +66,8 @@
             .build();
     LegacyDesugaredLibrarySpecification specification =
         new LegacyDesugaredLibrarySpecification(
-            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation);
-    setDesugaredLibrarySpecificationForTesting(options, specification);
+            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation, options.itemFactory);
+    options.desugaredLibrarySpecification = specification;
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 957515a..aa97e55 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -87,10 +87,9 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                setDesugaredLibrarySpecificationForTesting(
-                    opt,
+                opt.desugaredLibrarySpecification =
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary)))
+                        opt, false, parameters, supportAllCallbacksFromLibrary))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
@@ -152,10 +151,9 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                setDesugaredLibrarySpecificationForTesting(
-                    opt,
+                opt.desugaredLibrarySpecification =
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary)))
+                        opt, false, parameters, supportAllCallbacksFromLibrary))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
index b966b00..6b7d86e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
@@ -71,10 +71,9 @@
                         this.buildDesugaredLibrary(
                             api,
                             opt ->
-                                setDesugaredLibrarySpecificationForTesting(
-                                    opt,
+                                opt.desugaredLibrarySpecification =
                                     configurationWithSupportAllCallbacksFromLibrary(
-                                        opt, true, parameters, supportAllCallbacksFromLibrary))));
+                                        opt, true, parameters, supportAllCallbacksFromLibrary)));
                     return desugaredLibBox.get();
                   },
                   AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
index aaab5cb..a669527 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
@@ -58,7 +58,6 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(BaselineExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
-        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
@@ -99,7 +98,6 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(TryCatchExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
-        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 3b6ae0a..2bd50b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -270,7 +270,6 @@
               .addProgramFiles(testNGSupportProgramFiles())
               .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
               .addLibraryFiles(getLibraryFile())
-              .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
               .setMinApi(parameters.getApiLevel())
               .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
               .allowStdoutMessages()
@@ -304,7 +303,6 @@
           .addProgramFiles(testNGSupportProgramFiles())
           .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
           .addLibraryFiles(getLibraryFile())
-          .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
           .setMinApi(parameters.getApiLevel())
           .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
           .compile()
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index bf94752..f3103f1 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -12,13 +12,10 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
 import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
 import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -78,27 +75,11 @@
                 com.android.tools.r8.memberrebinding.b135627418.runtime.InsetDrawable.class)
             .setMinApi(parameters.getRuntime())
             .addOptionsModification(
-                options -> {
-                  options.desugaredLibrarySpecification =
-                      LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(
-                          ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
-                          options);
-                  DexType type =
-                      options
-                          .dexItemFactory()
-                          .createType(
-                              DescriptorUtils.javaTypeToDescriptor(
-                                  packageName + ".runtime.InsetDrawable"));
-                  DexType rewrittenType =
-                      options
-                          .dexItemFactory()
-                          .createType(
-                              DescriptorUtils.javaTypeToDescriptor(
-                                  packageName + ".library.InsetDrawable"));
-                  options.machineDesugaredLibrarySpecification =
-                      MachineDesugaredLibrarySpecification.withOnlyRewriteTypeForTesting(
-                          ImmutableMap.of(type, rewrittenType));
-                })
+                options ->
+                    options.desugaredLibrarySpecification =
+                        LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(
+                            ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
+                            options))
             .compile();
 
     testForR8(parameters.getBackend())