R8 desugared library first support

- Clean-up specialLibraryConfiguration into a single class.
- First test for R8+desugared lib.
- Support for Naming lens doing both minification and
  PrefixRewritting.

Bug:134732760
Change-Id: I8ed7fa16b3917ac1621009f535f0c766d1dedae9
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index f073650..1088e60 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -200,7 +200,7 @@
               null,
               GraphLense.getIdentityLense(),
               PrefixRewritingNamingLens.createPrefixRewritingNamingLens(
-                  app, converter.getAdditionalRewritePrefix()),
+                  options, converter.getAdditionalRewritePrefix()),
               null)
           .write(executor);
       options.printWarnings();
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6905dbf..717a26f 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -11,12 +11,8 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
 import java.util.Collection;
-import java.util.List;
-import java.util.Map;
 import java.util.function.BiPredicate;
 
 /**
@@ -257,125 +253,8 @@
     super(printHelp, printVersion);
   }
 
-  private Map<String, String> buildPrefixRewritingForProgramCompilation() {
-    return ImmutableMap.<String, String>builder()
-        // --rewrite_core_library_prefix.
-        // Following flags are ignored (already desugared).
-        // .put("java.lang.Double8", "j$.lang.Double8")
-        // .put("java.lang.Integer8", "j$.lang.Integer8")
-        // .put("java.lang.Long8", "j$.lang.Long8")
-        // .put("java.lang.Math8", "j$.lang.Math8")
-        .put("java.time.", "j$.time.")
-        .put("java.util.stream.", "j$.util.stream.")
-        .put("java.util.function.", "j$.util.function.")
-        .put("java.util.Desugar", "j$.util.Desugar")
-        .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
-        .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
-        .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
-        .put("java.util.Objects", "j$.util.Objects")
-        .put("java.util.Optional", "j$.util.Optional")
-        .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
-        .put("java.util.Spliterator", "j$.util.Spliterator")
-        .put("java.util.StringJoiner", "j$.util.StringJoiner")
-        .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
-        .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
-        .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
-        .build();
-  }
-
-  protected Map<String, String> buildRetargetCoreLibraryMemberForProgramCompilation() {
-    // --retarget_core_library_member.
-    return ImmutableMap.<String, String>builder()
-        // We ignore the following flags required by Bazel because desugaring of these methods
-        // is done separately.
-        // .put("java.lang.Double#max", "java.lang.Double8")
-        // .put("java.lang.Double#min", "java.lang.Double8")
-        // .put("java.lang.Double#sum", "java.lang.Double8")
-        // .put("java.lang.Integer#max", "java.lang.Integer8")
-        // .put("java.lang.Integer#min", "java.lang.Integer8")
-        // .put("java.lang.Integer#sum", "java.lang.Integer8")
-        // .put("java.lang.Long#max", "java.lang.Long")
-        // .put("java.lang.Long#min", "java.lang.Long")
-        // .put("java.lang.Long#sum", "java.lang.Long")
-        // .put("java.lang.Math#toIntExact", "java.lang.Math8")
-        .put("java.util.Arrays#stream", "java.util.DesugarArrays")
-        .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
-        .put("java.util.Calendar#toInstant", "java.util.DesugarCalendar")
-        .put("java.util.Date#from", "java.util.DesugarDate")
-        .put("java.util.Date#toInstant", "java.util.DesugarDate")
-        .put("java.util.GregorianCalendar#from", "java.util.DesugarGregorianCalendar")
-        .put("java.util.GregorianCalendar#toZonedDateTime", "java.util.DesugarGregorianCalendar")
-        .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .build();
-  }
-
-  protected List<String> buildDontRewriteInvocations() {
-    // --dont_rewrite_core_library_invocation "java/util/Iterator#remove".
-    return ImmutableList.of("java.util.Iterator#remove");
-  }
-
-  protected Map<String, String> buildEmulateLibraryInterface() {
-    return ImmutableMap.<String, String>builder()
-        // --emulate_core_library_interface.
-        // Bazel flags.
-        .put("java.util.Map$Entry", "j$.util.Map$Entry")
-        .put("java.util.Collection", "j$.util.Collection")
-        .put("java.util.Map", "j$.util.Map")
-        .put("java.util.Iterator", "j$.util.Iterator")
-        .put("java.util.Comparator", "j$.util.Comparator")
-        // Extra flags: in R8 we marked as emulated all interfaces
-        // with default methods. Emulated interfaces have their
-        // companion class moved to j$ and have a dispatch class.
-        // Bazel instead analyzes the class hierarchy.
-        .put("java.util.List", "j$.util.List")
-        .put("java.util.SortedSet", "j$.util.SortedSet")
-        .put("java.util.Set", "j$.util.Set")
-        .put("java.util.concurrent.ConcurrentMap", "j$.util.concurrent.ConcurrentMap")
-        .build();
-  }
-
   private void configureLibraryDesugaring(InternalOptions options) {
-    options.coreLibraryCompilation = false;
-    options.retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilation();
-    options.dontRewriteInvocations = buildDontRewriteInvocations();
-    options.rewritePrefix = buildPrefixRewritingForProgramCompilation();
-    options.emulateLibraryInterface = buildEmulateLibraryInterface();
+    SpecialLibraryConfiguration.configureLibraryDesugaringForProgramCompilation(options);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 587329e..4bf785c 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -75,7 +75,7 @@
               null,
               GraphLense.getIdentityLense(),
               PrefixRewritingNamingLens.createPrefixRewritingNamingLens(
-                  app, converter.getAdditionalRewritePrefix()),
+                  options, converter.getAdditionalRewritePrefix()),
               null)
           .write(executor);
       options.printWarnings();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index e6e49bc..3b23ef5 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -10,10 +10,6 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.util.List;
-import java.util.Map;
 
 /** Immutable command structure for an invocation of the {@link L8} libray compiler. */
 @Keep
@@ -74,7 +70,7 @@
       if (getSpecialLibraryConfiguration() == null) {
         reporter.error("L8 requires a special library configuration");
       } else if (!getSpecialLibraryConfiguration().equals("default")) {
-        reporter.error("L8 currently require special library configuration to be \"default\"");
+        reporter.error("L8 currently requires the special library configuration to be \"default\"");
       }
       if (getProgramConsumer() instanceof ClassFileConsumer) {
         reporter.error("L8 does not support compiling to Java class files");
@@ -141,154 +137,8 @@
     super(printHelp, printVersion);
   }
 
-  private Map<String, String> buildBackportCoreLibraryMembers() {
-    // R8 specific to deal with *8 removal.
-    return ImmutableMap.<String, String>builder()
-        .put("java.lang.Double8", "java.lang.Double")
-        .put("java.lang.Integer8", "java.lang.Integer")
-        .put("java.lang.Long8", "java.lang.Long")
-        .build();
-  }
-
-  private Map<String, String> buildRetargetCoreLibraryMemberForCoreLibCompilation() {
-    /*
-      --retarget_core_library_member.
-
-      The bazel configuration only have this single --retarget_core_library_member option:
-
-      --retarget_core_library_member \
-          "java/util/LinkedHashSet#spliterator->java/util/DesugarLinkedHashSet"
-
-      The configuration below is the full configuration for programs using the desugared library.
-      This should fine, as any calls to these re-targeted methods should be rewritten. The main
-      reason for adding all of them is that the additional files added to the desugared library
-      for running some of the JDK 11 tests use these APIs.
-    */
-    return ImmutableMap.<String, String>builder()
-        // We ignore the following flags required by Bazel because desugaring of these methods
-        // is done separately.
-        // .put("java.lang.Double#max", "java.lang.Double8")
-        // .put("java.lang.Double#min", "java.lang.Double8")
-        // .put("java.lang.Double#sum", "java.lang.Double8")
-        // .put("java.lang.Integer#max", "java.lang.Integer8")
-        // .put("java.lang.Integer#min", "java.lang.Integer8")
-        // .put("java.lang.Integer#sum", "java.lang.Integer8")
-        // .put("java.lang.Long#max", "java.lang.Long")
-        // .put("java.lang.Long#min", "java.lang.Long")
-        // .put("java.lang.Long#sum", "java.lang.Long")
-        // .put("java.lang.Math#toIntExact", "java.lang.Math8")
-        .put("java.util.Arrays#stream", "java.util.DesugarArrays")
-        .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
-        .put("java.util.Calendar#toInstant", "java.util.DesugarCalendar")
-        .put("java.util.Date#from", "java.util.DesugarDate")
-        .put("java.util.Date#toInstant", "java.util.DesugarDate")
-        .put("java.util.GregorianCalendar#from", "java.util.DesugarGregorianCalendar")
-        .put("java.util.GregorianCalendar#toZonedDateTime", "java.util.DesugarGregorianCalendar")
-        .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicInteger")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicLong#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicLong")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#getAndUpdate",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#updateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#getAndAccumulate",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .put(
-            "java.util.concurrent.atomic.AtomicReference#accumulateAndGet",
-            "java.util.concurrent.atomic.DesugarAtomicReference")
-        .build();
-  }
-
-  private List<String> buildDontRewriteInvocations() {
-    // --dont_rewrite_core_library_invocation "java/util/Iterator#remove".
-    return ImmutableList.of("java.util.Iterator#remove");
-  }
-
-  private Map<String, String> buildPrefixRewritingForCoreLibCompilation() {
-    return ImmutableMap.<String, String>builder()
-        // --rewrite_core_library_prefix.
-        // Extra flags for R8
-        .put("java.io.DesugarBufferedReader", "j$.io.DesugarBufferedReader")
-        .put("java.io.UncheckedIOException", "j$.io.UncheckedIOException")
-        // Bazel flags.
-        .put("java.lang.Double8", "j$.lang.Double8")
-        .put("java.lang.Integer8", "j$.lang.Integer8")
-        .put("java.lang.Long8", "j$.lang.Long8")
-        .put("java.lang.Math8", "j$.lang.Math8")
-        .put("java.time.", "j$.time.")
-        .put("java.util.stream.", "j$.util.stream.")
-        .put("java.util.function.", "j$.util.function.")
-        .put("java.util.Comparators", "j$.util.Comparators")
-        .put("java.util.Desugar", "j$.util.Desugar")
-        .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
-        .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
-        .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
-        .put("java.util.Objects", "j$.util.Objects")
-        .put("java.util.Optional", "j$.util.Optional")
-        .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
-        .put("java.util.SortedSet$1", "j$.util.SortedSet$1")
-        .put("java.util.Spliterator", "j$.util.Spliterator")
-        .put("java.util.StringJoiner", "j$.util.StringJoiner")
-        .put("java.util.Tripwire", "j$.util.Tripwire")
-        .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
-        .put("java.util.concurrent.DesugarUnsafe", "j$.util.concurrent.DesugarUnsafe")
-        .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
-        .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
-        .build();
-  }
-
-  private Map<String, String> buildEmulateLibraryInterface() {
-    return ImmutableMap.<String, String>builder()
-        // --emulate_core_library_interface.
-        // Bazel flags.
-        .put("java.util.Map$Entry", "j$.util.Map$Entry")
-        .put("java.util.Collection", "j$.util.Collection")
-        .put("java.util.Map", "j$.util.Map")
-        .put("java.util.Iterator", "j$.util.Iterator")
-        .put("java.util.Comparator", "j$.util.Comparator")
-        // Extra flags: in R8 we marked as emulated all interfaces
-        // with default methods. Emulated interfaces have their
-        // companion class moved to j$ and have a dispatch class.
-        // Bazel instead analyzes the class hierarchy.
-        .put("java.util.List", "j$.util.List")
-        .put("java.util.SortedSet", "j$.util.SortedSet")
-        .put("java.util.Set", "j$.util.Set")
-        .put("java.util.concurrent.ConcurrentMap", "j$.util.concurrent.ConcurrentMap")
-        .build();
-  }
-
   private void configureLibraryDesugaring(InternalOptions options) {
-    options.coreLibraryCompilation = true;
-    options.backportCoreLibraryMembers = buildBackportCoreLibraryMembers();
-    options.retargetCoreLibMember = buildRetargetCoreLibraryMemberForCoreLibCompilation();
-    options.dontRewriteInvocations = buildDontRewriteInvocations();
-    options.rewritePrefix = buildPrefixRewritingForCoreLibCompilation();
-    options.emulateLibraryInterface = buildEmulateLibraryInterface();
+    SpecialLibraryConfiguration.configureLibraryDesugaringForLibraryCompilation(options);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7ad67a0..0ac2984 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -41,6 +41,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.Minifier;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.PrefixRewritingNamingLens;
 import com.android.tools.r8.naming.ProguardMapMinifier;
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.naming.SeedMapper;
@@ -97,6 +98,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -528,12 +530,14 @@
       appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLense()));
 
       timing.begin("Create IR");
+      Map<String, String> additionalRewritePrefix;
       Set<DexCallSite> desugaredCallSites;
       CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
       try {
         IRConverter converter = new IRConverter(appView, timing, printer, mainDexClasses);
         application = converter.optimize(executorService);
         desugaredCallSites = converter.getDesugaredCallSites();
+        additionalRewritePrefix = converter.getAdditionalRewritePrefix();
       } finally {
         timing.end();
       }
@@ -785,7 +789,8 @@
           appView,
           application.deadCode,
           appView.graphLense(),
-          namingLens,
+          PrefixRewritingNamingLens.createPrefixRewritingNamingLens(
+              options, additionalRewritePrefix, namingLens),
           options,
           proguardMapSupplier);
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a4194b2..b4043b2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -387,8 +387,9 @@
       if (getProgramConsumer() instanceof ClassFileConsumer && isMinApiLevelSet()) {
         reporter.error("R8 does not support --min-api when compiling to class files");
       }
-      if (getSpecialLibraryConfiguration() != null) {
-        reporter.error("R8 does not support special library configuration");
+      if (getSpecialLibraryConfiguration() != null
+          && !getSpecialLibraryConfiguration().equals("default")) {
+        reporter.error("R8 currently require the special library configuration to be \"default\"");
       }
       super.validate();
     }
@@ -830,10 +831,17 @@
     assert internal.retargetCoreLibMember.isEmpty();
     assert internal.backportCoreLibraryMembers.isEmpty();
     assert internal.dontRewriteInvocations.isEmpty();
+    if (getSpecialLibraryConfiguration() != null) {
+      configureLibraryDesugaring(internal);
+    }
 
     return internal;
   }
 
+  private void configureLibraryDesugaring(InternalOptions options) {
+    SpecialLibraryConfiguration.configureLibraryDesugaringForProgramCompilation(options);
+  }
+
   private static StringConsumer wrapStringConsumer(
       StringConsumer optionConsumer, boolean optionsFlag, Path optionFile) {
     if (optionsFlag) {
diff --git a/src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java b/src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java
new file mode 100644
index 0000000..6468d16
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/SpecialLibraryConfiguration.java
@@ -0,0 +1,264 @@
+// 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;
+
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+
+// TODO(134732760): This is still work in progress.
+// The SpecialLibraryConfiguration is a set of flags for experimentation
+// with the desugared JDK libraries which should be replaced by a D8/R8
+// API level flag --coreLibraryDescriptor
+public class SpecialLibraryConfiguration {
+
+  private static Map<String, String> buildPrefixRewritingForProgramCompilation() {
+    return ImmutableMap.<String, String>builder()
+        // --rewrite_core_library_prefix.
+        // Following flags are ignored (already desugared).
+        // .put("java.lang.Double8", "j$.lang.Double8")
+        // .put("java.lang.Integer8", "j$.lang.Integer8")
+        // .put("java.lang.Long8", "j$.lang.Long8")
+        // .put("java.lang.Math8", "j$.lang.Math8")
+        .put("java.time.", "j$.time.")
+        .put("java.util.stream.", "j$.util.stream.")
+        .put("java.util.function.", "j$.util.function.")
+        .put("java.util.Desugar", "j$.util.Desugar")
+        .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
+        .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
+        .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
+        .put("java.util.Objects", "j$.util.Objects")
+        .put("java.util.Optional", "j$.util.Optional")
+        .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
+        .put("java.util.Spliterator", "j$.util.Spliterator")
+        .put("java.util.StringJoiner", "j$.util.StringJoiner")
+        .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
+        .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
+        .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
+        .build();
+  }
+
+  private static Map<String, String> buildPrefixRewritingForCoreLibCompilation() {
+    return ImmutableMap.<String, String>builder()
+        // --rewrite_core_library_prefix.
+        // Extra flags for R8
+        .put("java.io.DesugarBufferedReader", "j$.io.DesugarBufferedReader")
+        .put("java.io.UncheckedIOException", "j$.io.UncheckedIOException")
+        // Bazel flags.
+        .put("java.lang.Double8", "j$.lang.Double8")
+        .put("java.lang.Integer8", "j$.lang.Integer8")
+        .put("java.lang.Long8", "j$.lang.Long8")
+        .put("java.lang.Math8", "j$.lang.Math8")
+        .put("java.time.", "j$.time.")
+        .put("java.util.stream.", "j$.util.stream.")
+        .put("java.util.function.", "j$.util.function.")
+        .put("java.util.Comparators", "j$.util.Comparators")
+        .put("java.util.Desugar", "j$.util.Desugar")
+        .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
+        .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
+        .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
+        .put("java.util.Objects", "j$.util.Objects")
+        .put("java.util.Optional", "j$.util.Optional")
+        .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
+        .put("java.util.SortedSet$1", "j$.util.SortedSet$1")
+        .put("java.util.Spliterator", "j$.util.Spliterator")
+        .put("java.util.StringJoiner", "j$.util.StringJoiner")
+        .put("java.util.Tripwire", "j$.util.Tripwire")
+        .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
+        .put("java.util.concurrent.DesugarUnsafe", "j$.util.concurrent.DesugarUnsafe")
+        .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
+        .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
+        .build();
+  }
+
+  private static Map<String, String> buildRetargetCoreLibraryMemberForProgramCompilation() {
+    // --retarget_core_library_member.
+    return ImmutableMap.<String, String>builder()
+        // We ignore the following flags required by Bazel because desugaring of these methods
+        // is done separately.
+        // .put("java.lang.Double#max", "java.lang.Double8")
+        // .put("java.lang.Double#min", "java.lang.Double8")
+        // .put("java.lang.Double#sum", "java.lang.Double8")
+        // .put("java.lang.Integer#max", "java.lang.Integer8")
+        // .put("java.lang.Integer#min", "java.lang.Integer8")
+        // .put("java.lang.Integer#sum", "java.lang.Integer8")
+        // .put("java.lang.Long#max", "java.lang.Long")
+        // .put("java.lang.Long#min", "java.lang.Long")
+        // .put("java.lang.Long#sum", "java.lang.Long")
+        // .put("java.lang.Math#toIntExact", "java.lang.Math8")
+        .put("java.util.Arrays#stream", "java.util.DesugarArrays")
+        .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
+        .put("java.util.Calendar#toInstant", "java.util.DesugarCalendar")
+        .put("java.util.Date#from", "java.util.DesugarDate")
+        .put("java.util.Date#toInstant", "java.util.DesugarDate")
+        .put("java.util.GregorianCalendar#from", "java.util.DesugarGregorianCalendar")
+        .put("java.util.GregorianCalendar#toZonedDateTime", "java.util.DesugarGregorianCalendar")
+        .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .build();
+  }
+
+  private static Map<String, String> buildRetargetCoreLibraryMemberForCoreLibCompilation() {
+    /*
+      --retarget_core_library_member.
+
+      The bazel configuration only have this single --retarget_core_library_member option:
+
+      --retarget_core_library_member \
+          "java/util/LinkedHashSet#spliterator->java/util/DesugarLinkedHashSet"
+
+      The configuration below is the full configuration for programs using the desugared library.
+      This should fine, as any calls to these re-targeted methods should be rewritten. The main
+      reason for adding all of them is that the additional files added to the desugared library
+      for running some of the JDK 11 tests use these APIs.
+    */
+    return ImmutableMap.<String, String>builder()
+        // We ignore the following flags required by Bazel because desugaring of these methods
+        // is done separately.
+        // .put("java.lang.Double#max", "java.lang.Double8")
+        // .put("java.lang.Double#min", "java.lang.Double8")
+        // .put("java.lang.Double#sum", "java.lang.Double8")
+        // .put("java.lang.Integer#max", "java.lang.Integer8")
+        // .put("java.lang.Integer#min", "java.lang.Integer8")
+        // .put("java.lang.Integer#sum", "java.lang.Integer8")
+        // .put("java.lang.Long#max", "java.lang.Long")
+        // .put("java.lang.Long#min", "java.lang.Long")
+        // .put("java.lang.Long#sum", "java.lang.Long")
+        // .put("java.lang.Math#toIntExact", "java.lang.Math8")
+        .put("java.util.Arrays#stream", "java.util.DesugarArrays")
+        .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
+        .put("java.util.Calendar#toInstant", "java.util.DesugarCalendar")
+        .put("java.util.Date#from", "java.util.DesugarDate")
+        .put("java.util.Date#toInstant", "java.util.DesugarDate")
+        .put("java.util.GregorianCalendar#from", "java.util.DesugarGregorianCalendar")
+        .put("java.util.GregorianCalendar#toZonedDateTime", "java.util.DesugarGregorianCalendar")
+        .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicInteger")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicLong#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicLong")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#getAndUpdate",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#updateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#getAndAccumulate",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .put(
+            "java.util.concurrent.atomic.AtomicReference#accumulateAndGet",
+            "java.util.concurrent.atomic.DesugarAtomicReference")
+        .build();
+  }
+
+  private static List<String> buildDontRewriteInvocations() {
+    // --dont_rewrite_core_library_invocation "java/util/Iterator#remove".
+    return ImmutableList.of("java.util.Iterator#remove");
+  }
+
+  private static Map<String, String> buildEmulateLibraryInterface() {
+    return ImmutableMap.<String, String>builder()
+        // --emulate_core_library_interface.
+        // Bazel flags.
+        .put("java.util.Map$Entry", "j$.util.Map$Entry")
+        .put("java.util.Collection", "j$.util.Collection")
+        .put("java.util.Map", "j$.util.Map")
+        .put("java.util.Iterator", "j$.util.Iterator")
+        .put("java.util.Comparator", "j$.util.Comparator")
+        // Extra flags: in R8 we marked as emulated all interfaces
+        // with default methods. Emulated interfaces have their
+        // companion class moved to j$ and have a dispatch class.
+        // Bazel instead analyzes the class hierarchy.
+        .put("java.util.List", "j$.util.List")
+        .put("java.util.SortedSet", "j$.util.SortedSet")
+        .put("java.util.Set", "j$.util.Set")
+        .put("java.util.concurrent.ConcurrentMap", "j$.util.concurrent.ConcurrentMap")
+        .build();
+  }
+
+  private static Map<String, String> buildBackportCoreLibraryMembers() {
+    // R8 specific to deal with *8 removal.
+    return ImmutableMap.<String, String>builder()
+        .put("java.lang.Double8", "java.lang.Double")
+        .put("java.lang.Integer8", "java.lang.Integer")
+        .put("java.lang.Long8", "java.lang.Long")
+        .build();
+  }
+
+  public static void configureLibraryDesugaringForProgramCompilation(InternalOptions options) {
+    options.coreLibraryCompilation = false;
+    options.retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilation();
+    options.dontRewriteInvocations = buildDontRewriteInvocations();
+    options.rewritePrefix = buildPrefixRewritingForProgramCompilation();
+    options.emulateLibraryInterface = buildEmulateLibraryInterface();
+  }
+
+  public static void configureLibraryDesugaringForLibraryCompilation(InternalOptions options) {
+    options.coreLibraryCompilation = true;
+    options.backportCoreLibraryMembers = buildBackportCoreLibraryMembers();
+    options.retargetCoreLibMember = buildRetargetCoreLibraryMemberForCoreLibCompilation();
+    options.dontRewriteInvocations = buildDontRewriteInvocations();
+    options.rewritePrefix = buildPrefixRewritingForCoreLibCompilation();
+    options.emulateLibraryInterface = buildEmulateLibraryInterface();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index a0129de..53f457f 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -105,6 +105,16 @@
   }
 
   @Override
+  public boolean verifyNoOverlap(Map<DexType, DexString> map) {
+    for (DexType alreadyRenamedInOtherLens : map.keySet()) {
+      assert !renaming.containsKey(alreadyRenamedInOtherLens);
+      assert !renaming.containsKey(
+          appView.dexItemFactory().createType(map.get(alreadyRenamedInOtherLens)));
+    }
+    return true;
+  }
+
+  @Override
   void forAllRenamedTypes(Consumer<DexType> consumer) {
     DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
         .forEach(consumer);
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 2dbf703..f1a7d84 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -29,14 +29,14 @@
 import java.util.function.Predicate;
 
 /**
- * Implements a translation of the Dex graph from original names to new names produced by
- * the {@link Minifier}.
- * <p>
- * The minifier does not actually rename classes and members but instead only produces a mapping
+ * Implements a translation of the Dex graph from original names to new names produced by the {@link
+ * Minifier}.
+ *
+ * <p>The minifier does not actually rename classes and members but instead only produces a mapping
  * from original ids to renamed ids. When writing the file, the graph has to be interpreted with
  * that mapping in mind, i.e., it should be looked at only through this lens.
- * <p>
- * The translation relies on members being statically dispatched to actual definitions, as done
+ *
+ * <p>The translation relies on members being statically dispatched to actual definitions, as done
  * by the {@link MemberRebindingAnalysis} optimization.
  */
 public abstract class NamingLens {
@@ -99,6 +99,8 @@
     return dexItemFactory.createType(lookupDescriptor(type));
   }
 
+  public abstract boolean verifyNoOverlap(Map<DexType, DexString> map);
+
   public boolean hasPrefixRewritingLogic() {
     return false;
   }
@@ -127,10 +129,9 @@
 
   /**
    * Checks whether the target will be translated properly by this lense.
-   * <p>
-   * Normally, this means that the target corresponds to an actual definition that has been
-   * renamed. For identity renamings, we are more relaxed, as no targets will be translated
-   * anyway.
+   *
+   * <p>Normally, this means that the target corresponds to an actual definition that has been
+   * renamed. For identity renamings, we are more relaxed, as no targets will be translated anyway.
    */
   public abstract boolean checkTargetCanBeTranslated(DexMethod item);
 
@@ -194,6 +195,11 @@
     }
 
     @Override
+    public boolean verifyNoOverlap(Map<DexType, DexString> map) {
+      return true;
+    }
+
+    @Override
     public String lookupPackageName(String packageName) {
       return packageName;
     }
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index ed5c1bc..6b24a34 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -4,8 +4,7 @@
 
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
@@ -26,21 +25,33 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 // Naming lens for rewriting type prefixes.
 public class PrefixRewritingNamingLens extends NamingLens {
-  Map<DexType, DexString> classRenaming = new IdentityHashMap<>();
+  final Map<DexType, DexString> classRenaming = new IdentityHashMap<>();
+  final NamingLens namingLens;
+  final InternalOptions options;
 
   public static NamingLens createPrefixRewritingNamingLens(
-      DexApplication app, Map<String, String> additionalRewritePrefix) {
-    if (app.options.rewritePrefix.isEmpty() && additionalRewritePrefix.isEmpty()) {
-      return NamingLens.getIdentityLens();
+      InternalOptions options, Map<String, String> additionalRewritePrefix) {
+    return createPrefixRewritingNamingLens(
+        options, additionalRewritePrefix, NamingLens.getIdentityLens());
+  }
+
+  public static NamingLens createPrefixRewritingNamingLens(
+      InternalOptions options, Map<String, String> additionalRewritePrefix, NamingLens namingLens) {
+    if (options.rewritePrefix.isEmpty() && additionalRewritePrefix.isEmpty()) {
+      return namingLens;
     }
-    return new PrefixRewritingNamingLens(app, additionalRewritePrefix);
+    return new PrefixRewritingNamingLens(namingLens, options, additionalRewritePrefix);
   }
 
   public PrefixRewritingNamingLens(
-      DexApplication app, Map<String, String> additionalRewritePrefix) {
+      NamingLens namingLens, InternalOptions options, Map<String, String> additionalRewritePrefix) {
+    this.namingLens = namingLens;
+    this.options = options;
     // Create a map of descriptor prefix remappings.
     Map<String, String> descriptorPrefixRewriting = new TreeMap<>(Collections.reverseOrder());
     BiConsumer<String, String> lambda =
@@ -48,12 +59,11 @@
             descriptorPrefixRewriting.put(
                 "L" + DescriptorUtils.getBinaryNameFromJavaType(from),
                 "L" + DescriptorUtils.getBinaryNameFromJavaType(to));
-    app.options.rewritePrefix.forEach(lambda);
+    options.rewritePrefix.forEach(lambda);
     additionalRewritePrefix.forEach(lambda);
-
     // Run over all types and remap types with matching prefixes.
     // TODO(134732760): Use a more efficient data structure (prefix tree/trie).
-    DexItemFactory itemFactory = app.options.itemFactory;
+    DexItemFactory itemFactory = options.itemFactory;
     itemFactory.forAllTypes(
         type -> {
           String descriptor = type.descriptor.toString();
@@ -75,6 +85,8 @@
             }
           }
         });
+    // Verify that no type would have been renamed by both lenses.
+    assert namingLens.verifyNoOverlap(classRenaming);
   }
 
   @Override
@@ -89,53 +101,103 @@
 
   @Override
   public DexString lookupDescriptor(DexType type) {
-    return classRenaming.getOrDefault(type, type.descriptor);
+    return classRenaming.getOrDefault(type, namingLens.lookupDescriptor(type));
   }
 
   @Override
   public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
-    return attribute.getInnerName();
+    if (classRenaming.containsKey(attribute.getInner())) {
+      // Prefix rewriting does not influence the inner name.
+      return attribute.getInnerName();
+    }
+    return namingLens.lookupInnerName(attribute, options);
   }
 
   @Override
   public DexString lookupName(DexMethod method) {
-    return method.name;
+    if (classRenaming.containsKey(method.holder)) {
+      // Prefix rewriting does not influence the method name.
+      return method.name;
+    }
+    return namingLens.lookupName(method);
   }
 
   @Override
   public DexString lookupMethodName(DexCallSite callSite) {
-    return callSite.methodName;
+    if (classRenaming.containsKey(callSite.bootstrapMethod.rewrittenTarget.holder)) {
+      // Prefix rewriting does not influence the inner name.
+      return callSite.methodName;
+    }
+    return namingLens.lookupMethodName(callSite);
   }
 
   @Override
   public DexString lookupName(DexField field) {
-    return field.name;
+    if (classRenaming.containsKey(field.holder)) {
+      // Prefix rewriting does not influence the field name.
+      return field.name;
+    }
+    return namingLens.lookupName(field);
+  }
+
+  @Override
+  public boolean verifyNoOverlap(Map<DexType, DexString> map) {
+    throw new Unreachable("Multiple prefix rewriting lens not supported.");
   }
 
   @Override
   public String lookupPackageName(String packageName) {
-    throw new Unimplemented();
+    // Used for resource shrinking.
+    // Desugared libraries do not have resources.
+    // Hence this call is necessarily for the minifyingLens.
+    // TODO(b/134732760): This assertion does not hold with ressources with renamed prefixes.
+    // Write a test where the assertion does not hold and fix it.
+    assert verifyNotPrefixRewrittenPackage(packageName);
+    return namingLens.lookupPackageName(packageName);
+  }
+
+  private boolean verifyNotPrefixRewrittenPackage(String packageName) {
+    for (DexType dexType : classRenaming.keySet()) {
+      assert !dexType.getPackageDescriptor().equals(packageName);
+    }
+    return true;
   }
 
   @Override
   public void forAllRenamedTypes(Consumer<DexType> consumer) {
-    throw new Unimplemented();
+    // Used for printing the applyMapping map.
+    // If compiling the program using a desugared library, nothing needs to be printed.
+    // If compiling the desugared library, the mapping needs to be printed.
+    // When debugging the program, both mapping files need to be merged.
+    if (options.coreLibraryCompilation) {
+      classRenaming.keySet().forEach(consumer);
+    }
+    namingLens.forAllRenamedTypes(consumer);
   }
 
   @Override
   public <T extends DexItem> Map<String, T> getRenamedItems(
       Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
+    Map<String, T> renamedItemsPrefixRewritting;
     if (clazz == DexType.class) {
-      return classRenaming.keySet().stream()
-          .filter(item -> predicate.test(clazz.cast(item)))
-          .map(clazz::cast)
-          .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+      renamedItemsPrefixRewritting =
+          classRenaming.keySet().stream()
+              .filter(item -> predicate.test(clazz.cast(item)))
+              .map(clazz::cast)
+              .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+    } else {
+      renamedItemsPrefixRewritting = ImmutableMap.of();
     }
-    return ImmutableMap.of();
+    Map<String, T> renamedItemsMinifier = namingLens.getRenamedItems(clazz, predicate, namer);
+    // The Collector throws an exception for duplicated keys.
+    return Stream.concat(
+            renamedItemsPrefixRewritting.entrySet().stream(),
+            renamedItemsMinifier.entrySet().stream())
+        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
   }
 
   @Override
   public boolean checkTargetCanBeTranslated(DexMethod item) {
-    return true;
+    return namingLens.checkTargetCanBeTranslated(item);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/Box.java b/src/main/java/com/android/tools/r8/utils/Box.java
index ecf2b6f..8d3ec1e 100644
--- a/src/main/java/com/android/tools/r8/utils/Box.java
+++ b/src/main/java/com/android/tools/r8/utils/Box.java
@@ -8,6 +8,12 @@
 
   private T value;
 
+  public Box() {}
+
+  public Box(T initialValue) {
+    set(initialValue);
+  }
+
   public T get() {
     return value;
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/EmulateLibraryInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/EmulateLibraryInterfaceTest.java
index ee5a8d0..7f748f4 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/EmulateLibraryInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/EmulateLibraryInterfaceTest.java
@@ -8,9 +8,13 @@
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
@@ -20,6 +24,8 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -135,9 +141,9 @@
   }
 
   @Test
-  public void testProgram() throws Exception {
+  public void testProgramD8() throws Exception {
     Assume.assumeTrue("No desugaring for high API levels", requiresCoreLibDesugaring(parameters));
-    String[] keepRulesHolder = new String[] {""};
+    Box<String> keepRulesHolder = new Box<>("");
     D8TestRunResult d8TestRunResult =
         testForD8()
             .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
@@ -146,7 +152,7 @@
             .addOptionsModification(
                 options ->
                     options.testing.desugaredLibraryKeepRuleConsumer =
-                        (string, handler) -> keepRulesHolder[0] += string)
+                        (string, handler) -> keepRulesHolder.set(keepRulesHolder.get() + string))
             .enableCoreLibraryDesugaring()
             .compile()
             .inspect(this::checkRewrittenInvokes)
@@ -154,7 +160,7 @@
             .run(parameters.getRuntime(), "stream.TestClass")
             .assertSuccess();
     assertLines2By2Correct(d8TestRunResult.getStdOut());
-    assertGeneratedKeepRulesAreCorrect(keepRulesHolder[0]);
+    assertGeneratedKeepRulesAreCorrect(keepRulesHolder.get());
     String stdErr = d8TestRunResult.getStdErr();
     if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
       // Flaky: There might be a missing method on lambda deserialization.
@@ -166,6 +172,42 @@
     }
   }
 
+  @Test
+  public void testProgramR8() throws Exception {
+    Assume.assumeTrue("No desugaring for high API levels", requiresCoreLibDesugaring(parameters));
+    Box<String> keepRulesHolder = new Box<>("");
+    for (Boolean minifying : BooleanUtils.values()) {
+      R8TestRunResult r8TestRunResult =
+          testForR8(parameters.getBackend())
+              .minification(minifying)
+              .addKeepMainRule("stream.TestClass")
+              .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .setMinApi(parameters.getApiLevel())
+              .addOptionsModification(
+                  options ->
+                      options.testing.desugaredLibraryKeepRuleConsumer =
+                          (string, handler) -> keepRulesHolder.set(keepRulesHolder.get() + string))
+              .enableCoreLibraryDesugaring()
+              .compile()
+              .inspect(this::checkRewrittenInvokes)
+              .addRunClasspathFiles(buildDesugaredLibrary(parameters.getApiLevel()))
+              .run(parameters.getRuntime(), "stream.TestClass")
+              .assertSuccess();
+      assertLines2By2Correct(r8TestRunResult.getStdOut());
+      assertGeneratedKeepRulesAreCorrect(keepRulesHolder.get());
+      if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+        // Flaky: There might be a missing method on lambda deserialization.
+        r8TestRunResult.assertStderrMatches(
+            anyOf(
+                not(containsString("Could not find method")),
+                containsString("Could not find method java.lang.invoke.SerializedLambda")));
+      } else {
+        r8TestRunResult.assertStderrMatches(not(containsString("Could not find method")));
+      }
+    }
+  }
+
   private void assertGeneratedKeepRulesAreCorrect(String keepRules) {
     String expectedResult =
         StringUtils.lines(